[clang] [llvm] [FlowSensitive] [StatusOr] [1/N] Add minimal model (PR #162932)
Florian Mayer via llvm-commits
llvm-commits at lists.llvm.org
Fri Oct 10 16:33:48 PDT 2025
https://github.com/fmayer updated https://github.com/llvm/llvm-project/pull/162932
>From 92d4136ee576acf006df02d0f28d23e5870db646 Mon Sep 17 00:00:00 2001
From: Florian Mayer <fmayer at google.com>
Date: Fri, 10 Oct 2025 15:15:03 -0700
Subject: [PATCH 1/3] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20in?=
=?UTF-8?q?itial=20version?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Created using spr 1.3.4
---
.../Models/UncheckedStatusOrAccessModel.h | 114 +
.../FlowSensitive/Models/CMakeLists.txt | 1 +
.../Models/UncheckedStatusOrAccessModel.cpp | 284 +
.../Analysis/FlowSensitive/CMakeLists.txt | 2 +
.../UncheckedStatusOrAccessModelTest.cpp | 34 +
...ncheckedStatusOrAccessModelTestFixture.cpp | 5231 +++++++++++++++++
.../UncheckedStatusOrAccessModelTestFixture.h | 159 +
.../Analysis/FlowSensitive/Models/BUILD.gn | 1 +
.../unittests/Analysis/FlowSensitive/BUILD.gn | 2 +
9 files changed, 5828 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..ed82907f4867d
--- /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 declation of `absl::StatusOr<T>` and bind `T` to "T".
+clang::ast_matchers::DeclarationMatcher statusOrClass();
+// Match declation 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 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..eab1db8330d5e
--- /dev/null
+++ b/clang/lib/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.cpp
@@ -0,0 +1,284 @@
+//===- 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/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 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 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 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 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);
+}
+
+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)
+ .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 3bd4a6e21bee7..0fd136fa77ca9 100644
--- a/clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
+++ b/clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
@@ -24,6 +24,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..af30e98c51c56
--- /dev/null
+++ b/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp
@@ -0,0 +1,5231 @@
+//===- 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 "llvm/Support/ErrorHandling.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "gtest/gtest.h"
+
+namespace clang::dataflow::statusor_model {
+namespace {
+
+static constexpr const char *kAbslDefsHeader = R"cc(
+ // Contains minimal mock declarations of common entities from //base, //util
+ // and //testing.
+
+#pragma clang system_header
+
+#ifndef BASE_DEFS_H_
+#define BASE_DEFS_H_
+
+#include "absl_type_traits.h"
+#include "std_type_traits.h"
+#include "stdlib_defs.h"
+#include "string_defs.h"
+
+ namespace absl {
+ struct in_place_t {};
+
+ constexpr in_place_t in_place;
+
+ struct nullopt_t {
+ constexpr explicit nullopt_t() {}
+ };
+
+ constexpr nullopt_t nullopt;
+ } // namespace absl
+
+ namespace std {
+
+ struct in_place_t {};
+
+ constexpr in_place_t in_place;
+
+ struct nullopt_t {
+ constexpr explicit nullopt_t() {}
+ };
+
+ constexpr nullopt_t nullopt;
+
+ template <class _Tp>
+ struct __optional_destruct_base {
+ constexpr void reset() noexcept;
+ };
+
+ template <class _Tp>
+ struct __optional_storage_base : __optional_destruct_base<_Tp> {
+ constexpr bool has_value() const noexcept;
+ };
+
+ template <typename _Tp>
+ class optional : private __optional_storage_base<_Tp> {
+ using __base = __optional_storage_base<_Tp>;
+
+ public:
+ using value_type = _Tp;
+
+ private:
+ struct _CheckOptionalArgsConstructor {
+ template <class _Up>
+ static constexpr bool __enable_implicit() {
+ return is_constructible_v<_Tp, _Up&&> && is_convertible_v<_Up&&, _Tp>;
+ }
+
+ template <class _Up>
+ static constexpr bool __enable_explicit() {
+ return is_constructible_v<_Tp, _Up&&> && !is_convertible_v<_Up&&, _Tp>;
+ }
+ };
+ template <class _Up>
+ using _CheckOptionalArgsCtor =
+ _If<_IsNotSame<__uncvref_t<_Up>, in_place_t>::value &&
+ _IsNotSame<__uncvref_t<_Up>, optional>::value,
+ _CheckOptionalArgsConstructor, __check_tuple_constructor_fail>;
+ template <class _QualUp>
+ struct _CheckOptionalLikeConstructor {
+ template <class _Up, class _Opt = optional<_Up>>
+ using __check_constructible_from_opt =
+ _Or<is_constructible<_Tp, _Opt&>, is_constructible<_Tp, _Opt const&>,
+ is_constructible<_Tp, _Opt&&>,
+ is_constructible<_Tp, _Opt const&&>, is_convertible<_Opt&, _Tp>,
+ is_convertible<_Opt const&, _Tp>, is_convertible<_Opt&&, _Tp>,
+ is_convertible<_Opt const&&, _Tp>>;
+ template <class _Up, class _QUp = _QualUp>
+ static constexpr bool __enable_implicit() {
+ return is_convertible<_QUp, _Tp>::value &&
+ !__check_constructible_from_opt<_Up>::value;
+ }
+ template <class _Up, class _QUp = _QualUp>
+ static constexpr bool __enable_explicit() {
+ return !is_convertible<_QUp, _Tp>::value &&
+ !__check_constructible_from_opt<_Up>::value;
+ }
+ };
+
+ template <class _Up, class _QualUp>
+ using _CheckOptionalLikeCtor =
+ _If<_And<_IsNotSame<_Up, _Tp>, is_constructible<_Tp, _QualUp>>::value,
+ _CheckOptionalLikeConstructor<_QualUp>,
+ __check_tuple_constructor_fail>;
+ template <class _Up, class _QualUp>
+ using _CheckOptionalLikeAssign =
+ _If<_And<_IsNotSame<_Up, _Tp>, is_constructible<_Tp, _QualUp>,
+ is_assignable<_Tp&, _QualUp>>::value,
+ _CheckOptionalLikeConstructor<_QualUp>,
+ __check_tuple_constructor_fail>;
+
+ public:
+ constexpr optional() noexcept {}
+ constexpr optional(const optional&) = default;
+ constexpr optional(optional&&) = default;
+ constexpr optional(nullopt_t) noexcept {}
+
+ template <class _InPlaceT, class... _Args,
+ class = std::enable_if_t<
+ _And<_IsSame<_InPlaceT, in_place_t>,
+ is_constructible<value_type, _Args...>>::value>>
+ constexpr explicit optional(_InPlaceT, _Args&&... __args);
+
+ template <class _Up, class... _Args,
+ class = std::enable_if_t<is_constructible_v<
+ value_type, initializer_list<_Up>&, _Args...>>>
+ constexpr explicit optional(in_place_t, initializer_list<_Up> __il,
+ _Args&&... __args);
+
+ template <class _Up = value_type,
+ std::enable_if_t<_CheckOptionalArgsCtor<
+ _Up>::template __enable_implicit<_Up>(),
+ int> = 0>
+ constexpr optional(_Up&& __v);
+
+ template <class _Up, std::enable_if_t<_CheckOptionalArgsCtor<_Up>::
+ template __enable_explicit<_Up>(),
+ int> = 0>
+ constexpr explicit optional(_Up&& __v);
+
+ template <class _Up,
+ std::enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>::
+ template __enable_implicit<_Up>(),
+ int> = 0>
+ constexpr optional(const optional<_Up>& __v);
+
+ template <class _Up,
+ std::enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>::
+ template __enable_explicit<_Up>(),
+ int> = 0>
+ constexpr explicit optional(const optional<_Up>& __v);
+
+ template <class _Up, std::enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::
+ template __enable_implicit<_Up>(),
+ int> = 0>
+ constexpr optional(optional<_Up>&& __v);
+
+ template <class _Up, std::enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::
+ template __enable_explicit<_Up>(),
+ int> = 0>
+ constexpr explicit optional(optional<_Up>&& __v);
+
+ constexpr optional& operator=(nullopt_t) noexcept;
+
+ optional& operator=(const optional&);
+
+ optional& operator=(optional&&);
+
+ template <class _Up = value_type,
+ class = std::enable_if_t<
+ _And<_IsNotSame<__uncvref_t<_Up>, optional>,
+ _Or<_IsNotSame<__uncvref_t<_Up>, value_type>,
+ _Not<is_scalar<value_type>>>,
+ is_constructible<value_type, _Up>,
+ is_assignable<value_type&, _Up>>::value>>
+ constexpr optional& operator=(_Up&& __v);
+
+ template <class _Up,
+ std::enable_if_t<_CheckOptionalLikeAssign<_Up, _Up const&>::
+ template __enable_assign<_Up>(),
+ int> = 0>
+ constexpr optional& operator=(const optional<_Up>& __v);
+
+ template <class _Up, std::enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::
+ template __enable_assign<_Up>(),
+ int> = 0>
+ constexpr optional& operator=(optional<_Up>&& __v);
+
+ const _Tp& operator*() const&;
+ _Tp& operator*() &;
+ const _Tp&& operator*() const&&;
+ _Tp&& operator*() &&;
+
+ const _Tp* operator->() const;
+ _Tp* operator->();
+
+ const _Tp& value() const&;
+ _Tp& value() &;
+ const _Tp&& value() const&&;
+ _Tp&& value() &&;
+
+ template <typename U>
+ constexpr _Tp value_or(U&& v) const&;
+ template <typename U>
+ _Tp value_or(U&& v) &&;
+
+ constexpr explicit operator bool() const noexcept;
+
+ using __base::has_value;
+
+ template <typename... Args>
+ _Tp& emplace(Args&&... args);
+
+ template <typename U, typename... Args>
+ _Tp& emplace(std::initializer_list<U> ilist, Args&&... args);
+
+ using __base::reset;
+
+ constexpr void swap(optional& __opt) noexcept;
+ };
+
+ template <class _Tp, class _Up>
+ constexpr std::enable_if_t<
+ is_convertible_v<decltype(declval<const _Tp&>() == declval<const _Up&>()),
+ bool>,
+ bool>
+ operator==(const optional<_Tp>& __x, const optional<_Up>& __y);
+
+ template <class _Tp, class _Up>
+ constexpr std::enable_if_t<
+ is_convertible_v<decltype(declval<const _Tp&>() != declval<const _Up&>()),
+ bool>,
+ bool>
+ operator!=(const optional<_Tp>& __x, const optional<_Up>& __y);
+
+ template <class _Tp>
+ constexpr bool operator==(const optional<_Tp>& __x, nullopt_t) noexcept;
+
+ template <class _Tp>
+ constexpr bool operator==(nullopt_t, const optional<_Tp>& __x) noexcept;
+
+ template <class _Tp>
+ constexpr bool operator!=(const optional<_Tp>& __x, nullopt_t) noexcept;
+
+ template <class _Tp>
+ constexpr bool operator!=(nullopt_t, const optional<_Tp>& __x) noexcept;
+
+ template <class _Tp, class _Up>
+ constexpr std::enable_if_t<
+ is_convertible_v<decltype(declval<const _Tp&>() == declval<const _Up&>()),
+ bool>,
+ bool>
+ operator==(const optional<_Tp>& __x, const _Up& __v);
+
+ template <class _Tp, class _Up>
+ constexpr std::enable_if_t<
+ is_convertible_v<decltype(declval<const _Tp&>() == declval<const _Up&>()),
+ bool>,
+ bool>
+ operator==(const _Tp& __v, const optional<_Up>& __x);
+
+ template <class _Tp, class _Up>
+ constexpr std::enable_if_t<
+ is_convertible_v<decltype(declval<const _Tp&>() != declval<const _Up&>()),
+ bool>,
+ bool>
+ operator!=(const optional<_Tp>& __x, const _Up& __v);
+
+ template <class _Tp, class _Up>
+ constexpr std::enable_if_t<
+ is_convertible_v<decltype(declval<const _Tp&>() != declval<const _Up&>()),
+ bool>,
+ bool>
+ operator!=(const _Tp& __v, const optional<_Up>& __x);
+
+ template <typename T>
+ constexpr optional<typename std::decay<T>::type> make_optional(T&& v) {
+ return optional<typename std::decay<T>::type>(std::forward<T>(v));
+ }
+
+ template <typename T, typename... Args>
+ constexpr optional<T> make_optional(Args&&... args) {
+ return optional<T>(in_place_t(), std::forward<Args>(args)...);
+ }
+
+ template <typename T, typename U, typename... Args>
+ constexpr optional<T> make_optional(std::initializer_list<U> il,
+ Args&&... args) {
+ return optional<T>(in_place_t(), il, std::forward<Args>(args)...);
+ }
+
+ } // namespace std
+
+ namespace absl {
+
+#define ABSL_PREDICT_FALSE(x) (__builtin_expect(false || (x), false))
+#define ABSL_PREDICT_TRUE(x) (__builtin_expect(false || (x), true))
+
+ struct VLogSite final {
+ explicit constexpr VLogSite(const char* f);
+ bool IsEnabled(int level);
+ };
+
+ namespace log_internal {
+ class LogMessage {
+ public:
+ LogMessage();
+ LogMessage& stream();
+ LogMessage& InternalStream();
+ LogMessage& WithVerbosity(int verboselevel);
+ template <typename T>
+ LogMessage& operator<<(const T&);
+ };
+ class LogMessageFatal : public LogMessage {
+ public:
+ LogMessageFatal();
+ ~LogMessageFatal() __attribute__((noreturn));
+ };
+ class LogMessageQuietlyFatal : public LogMessage {
+ public:
+ LogMessageQuietlyFatal();
+ ~LogMessageQuietlyFatal() __attribute__((noreturn));
+ };
+ class Voidify final {
+ public:
+ // This has to be an operator with a precedence lower than << but higher
+ // than
+ // ?:
+ template <typename T>
+ void operator&&(const T&) const&& {}
+ };
+ } // namespace log_internal
+ } // namespace absl
+
+#ifndef NULL
+#define NULL __null
+#endif
+ extern "C" void abort() {}
+#define ABSL_LOG_INTERNAL_LOG_INFO ::absl::log_internal::LogMessage()
+#define ABSL_LOG_INTERNAL_LOG_WARNING ::absl::log_internal::LogMessage()
+#define ABSL_LOG_INTERNAL_LOG_ERROR ::absl::log_internal::LogMessage()
+#define ABSL_LOG_INTERNAL_LOG_FATAL ::absl::log_internal::LogMessageFatal()
+#define ABSL_LOG_INTERNAL_LOG_QFATAL \
+ ::absl::log_internal::LogMessageQuietlyFatal()
+#define LOG(severity) ABSL_LOG_INTERNAL_LOG_##severity.InternalStream()
+
+#define PREDICT_FALSE(x) (__builtin_expect(x, 0))
+#define ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(lit) lit
+
+#define ABSL_LOG_INTERNAL_CHECK(failure_message) ABSL_LOG_INTERNAL_LOG_FATAL
+#define ABSL_LOG_INTERNAL_QCHECK(failure_message) ABSL_LOG_INTERNAL_LOG_QFATAL
+
+#define ABSL_LOG_INTERNAL_STATELESS_CONDITION(condition) \
+ switch (0) \
+ case 0: \
+ default: \
+ !(condition) ? (void)0 : ::absl::log_internal::Voidify() &&
+
+#define ABSL_LOG_INTERNAL_CONDITION_INFO(type, condition) \
+ ABSL_LOG_INTERNAL_##type##_CONDITION(condition)
+
+#define ABSL_LOG_INTERNAL_CONDITION_FATAL(type, condition) \
+ ABSL_LOG_INTERNAL_##type##_CONDITION(condition)
+
+#define ABSL_LOG_INTERNAL_CONDITION_QFATAL(type, condition) \
+ ABSL_LOG_INTERNAL_##type##_CONDITION(condition)
+
+#define ABSL_CHECK_IMPL(condition, condition_text) \
+ ABSL_LOG_INTERNAL_CONDITION_FATAL(STATELESS, \
+ ABSL_PREDICT_FALSE(!(condition))) \
+ ABSL_LOG_INTERNAL_CHECK(condition_text).InternalStream()
+
+#define ABSL_QCHECK_IMPL(condition, condition_text) \
+ ABSL_LOG_INTERNAL_CONDITION_QFATAL(STATELESS, \
+ ABSL_PREDICT_FALSE(!(condition))) \
+ ABSL_LOG_INTERNAL_QCHECK(condition_text).InternalStream()
+
+#define CHECK(condition) ABSL_CHECK_IMPL((condition), #condition)
+#define DCHECK(condition) CHECK(condition)
+#define QCHECK(condition) ABSL_QCHECK_IMPL((condition), #condition)
+
+#define ABSL_LOG_INTERNAL_MAX_LOG_VERBOSITY_CHECK(x)
+
+#define VLOG_IS_ON(verbose_level) \
+ (ABSL_LOG_INTERNAL_MAX_LOG_VERBOSITY_CHECK(verbose_level)[]() \
+ ->::absl::VLogSite * \
+ { \
+ static ::absl::VLogSite site(__FILE__); \
+ return &site; \
+ }() \
+ ->IsEnabled(verbose_level))
+
+#define ABSL_LOG_IF_IMPL(severity, condition) \
+ ABSL_LOG_INTERNAL_CONDITION##severity(STATELESS, condition) \
+ ABSL_LOG_INTERNAL_LOG##severity.InternalStream()
+
+#define ABSL_VLOG_IMPL(verbose_level) \
+ switch (const int absl_logging_internal_verbose_level = (verbose_level)) \
+ default: \
+ ABSL_LOG_IF_IMPL(_INFO, VLOG_IS_ON(absl_logging_internal_verbose_level)) \
+ .WithVerbosity(absl_logging_internal_verbose_level)
+
+#define VLOG(severity) ABSL_VLOG_IMPL(severity)
+
+ namespace absl {
+
+ template <typename T>
+ class StatusOr;
+ class Status;
+
+ namespace status_internal {
+ std::string* MakeCheckFailString(const absl::Status* status,
+ const char* prefix);
+ } // namespace status_internal
+
+ namespace log_internal {
+ template <class T>
+ const T& GetReferenceableValue(const T& t);
+ inline char GetReferenceableValue(char t) { return t; }
+ inline unsigned char GetReferenceableValue(unsigned char t) { return t; }
+ inline signed char GetReferenceableValue(signed char t) { return t; }
+ inline short GetReferenceableValue(short t) { return t; } // NOLINT
+ inline unsigned short GetReferenceableValue(unsigned short t) { // NOLINT
+ return t;
+ }
+ inline int GetReferenceableValue(int t) { return t; } // NOLINT
+ inline unsigned int GetReferenceableValue(unsigned int t) { return t; }
+ inline long GetReferenceableValue(long t) { return t; } // NOLINT
+ inline unsigned long GetReferenceableValue(unsigned long t) { // NOLINT
+ return t;
+ }
+ inline long long GetReferenceableValue(long long t) { return t; } // NOLINT
+ inline unsigned long long GetReferenceableValue( // NOLINT
+ unsigned long long t) { // NOLINT
+ return t;
+ }
+ inline const absl::Status* AsStatus(const absl::Status& s) { return &s; }
+ template <typename T>
+ const absl::Status* AsStatus(const absl::StatusOr<T>& s) {
+ return &s.status();
+ }
+ } // namespace log_internal
+ } // namespace absl
+ // TODO(tkd): this still doesn't allow operator<<, unlike the real CHECK_
+ // macros.
+#define ABSL_LOG_INTERNAL_CHECK_OP(name, op, val1, val2) \
+ while (char* _result = ::absl::log_internal::name##Impl( \
+ ::absl::log_internal::GetReferenceableValue(val1), \
+ ::absl::log_internal::GetReferenceableValue(val2), \
+ #val1 " " #op " " #val2)) \
+ (void)0
+#define ABSL_LOG_INTERNAL_QCHECK_OP(name, op, val1, val2) \
+ while (char* _result = ::absl::log_internal::name##Impl( \
+ ::absl::log_internal::GetReferenceableValue(val1), \
+ ::absl::log_internal::GetReferenceableValue(val2), \
+ #val1 " " #op " " #val2)) \
+ (void)0
+ namespace absl {
+ namespace log_internal {
+ template <class T1, class T2>
+ char* Check_NEImpl(const T1& v1, const T2& v2, const char* names);
+ template <class T1, class T2>
+ char* Check_EQImpl(const T1& v1, const T2& v2, const char* names);
+ template <class T1, class T2>
+ char* Check_LTImpl(const T1& v1, const T2& v2, const char* names);
+
+#define CHECK_EQ(a, b) ABSL_LOG_INTERNAL_CHECK_OP(Check_EQ, ==, a, b)
+#define CHECK_NE(a, b) ABSL_LOG_INTERNAL_CHECK_OP(Check_NE, !=, a, b)
+#define CHECK_LT(a, b) ABSL_LOG_INTERNAL_CHECK_OP(Check_EQ, <, a, b)
+
+#define QCHECK_EQ(a, b) ABSL_LOG_INTERNAL_QCHECK_OP(Check_EQ, ==, a, b)
+#define QCHECK_NE(a, b) ABSL_LOG_INTERNAL_QCHECK_OP(Check_NE, !=, a, b)
+ } // namespace log_internal
+ } // namespace absl
+
+#define CHECK_NOTNULL(x) CHECK((x) != nullptr)
+
+#define ABSL_LOG_INTERNAL_CHECK(failure_message) \
+ ::absl::log_internal::LogMessageFatal()
+#define ABSL_LOG_INTERNAL_QCHECK(failure_message) \
+ ::absl::log_internal::LogMessageQuietlyFatal()
+#define ABSL_LOG_INTERNAL_CHECK_OK(val) \
+ for (::std::pair<const ::absl::Status*, ::std::string*> \
+ absl_log_internal_check_ok_goo; \
+ absl_log_internal_check_ok_goo.first = \
+ ::absl::log_internal::AsStatus(val), \
+ absl_log_internal_check_ok_goo.second = \
+ ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok()) \
+ ? nullptr \
+ : ::absl::status_internal::MakeCheckFailString( \
+ absl_log_internal_check_ok_goo.first, \
+ ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(#val " is OK")), \
+ !ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok());) \
+ ABSL_LOG_INTERNAL_CHECK(*absl_log_internal_check_ok_goo.second) \
+ .InternalStream()
+#define ABSL_LOG_INTERNAL_QCHECK_OK(val) \
+ for (::std::pair<const ::absl::Status*, ::std::string*> \
+ absl_log_internal_check_ok_goo; \
+ absl_log_internal_check_ok_goo.first = \
+ ::absl::log_internal::AsStatus(val), \
+ absl_log_internal_check_ok_goo.second = \
+ ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok()) \
+ ? nullptr \
+ : ::absl::status_internal::MakeCheckFailString( \
+ absl_log_internal_check_ok_goo.first, \
+ ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(#val " is OK")), \
+ !ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok());) \
+ ABSL_LOG_INTERNAL_QCHECK(*absl_log_internal_check_ok_goo.second) \
+ .InternalStream()
+
+#define CHECK_OK(val) ABSL_LOG_INTERNAL_CHECK_OK(val)
+#define DCHECK_OK(val) ABSL_LOG_INTERNAL_CHECK_OK(val)
+#define QCHECK_OK(val) ABSL_LOG_INTERNAL_QCHECK_OK(val)
+
+ namespace testing {
+ struct AssertionResult {
+ template <typename T>
+ explicit AssertionResult(const T& res, bool enable_if = true) {}
+ ~AssertionResult();
+ operator bool() const;
+ template <typename T>
+ AssertionResult& operator<<(const T& value);
+ const char* failure_message() const;
+ };
+
+ class TestPartResult {
+ public:
+ enum Type { kSuccess, kNonFatalFailure, kFatalFailure, kSkip };
+ };
+
+ class Test {
+ public:
+ virtual ~Test() = default;
+
+ protected:
+ virtual void SetUp() {}
+ };
+
+ class Message {
+ public:
+ template <typename T>
+ Message& operator<<(const T& val);
+ };
+
+ namespace internal {
+ class AssertHelper {
+ public:
+ AssertHelper(TestPartResult::Type type, const char* file, int line,
+ const char* message);
+ void operator=(const Message& message) const;
+ };
+
+ class EqHelper {
+ public:
+ template <typename T1, typename T2>
+ static AssertionResult Compare(const char* lhx, const char* rhx,
+ const T1& lhs, const T2& rhs);
+ };
+
+#define GTEST_IMPL_CMP_HELPER_(op_name) \
+ template <typename T1, typename T2> \
+ AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \
+ const T1& val1, const T2& val2);
+
+ GTEST_IMPL_CMP_HELPER_(NE)
+ GTEST_IMPL_CMP_HELPER_(LE)
+ GTEST_IMPL_CMP_HELPER_(LT)
+ GTEST_IMPL_CMP_HELPER_(GE)
+ GTEST_IMPL_CMP_HELPER_(GT)
+
+#undef GTEST_IMPL_CMP_HELPER_
+
+ std::string GetBoolAssertionFailureMessage(
+ const AssertionResult& assertion_result, const char* expression_text,
+ const char* actual_predicate_value, const char* expected_predicate_value);
+
+ template <typename M>
+ class PredicateFormatterFromMatcher {
+ public:
+ template <typename T>
+ AssertionResult operator()(const char* value_text, const T& x) const;
+ };
+
+ template <typename M>
+ inline PredicateFormatterFromMatcher<M> MakePredicateFormatterFromMatcher(
+ M matcher) {
+ return PredicateFormatterFromMatcher<M>();
+ }
+ } // namespace internal
+
+ namespace status {
+ namespace internal_status {
+ class IsOkMatcher {};
+
+ class StatusIsMatcher {};
+
+ class CanonicalStatusIsMatcher {};
+
+ template <typename M>
+ class IsOkAndHoldsMatcher {};
+
+ } // namespace internal_status
+
+ internal_status::IsOkMatcher IsOk();
+
+ template <typename StatusCodeMatcher>
+ internal_status::StatusIsMatcher StatusIs(StatusCodeMatcher&& code_matcher);
+
+ template <typename StatusCodeMatcher>
+ internal_status::CanonicalStatusIsMatcher CanonicalStatusIs(
+ StatusCodeMatcher&& code_matcher);
+
+ template <typename InnerMatcher>
+ internal_status::IsOkAndHoldsMatcher<InnerMatcher> IsOkAndHolds(
+ InnerMatcher m);
+ } // namespace status
+
+ class IsTrueMatcher {};
+ IsTrueMatcher IsTrue();
+
+ class IsFalseMatcher {};
+ IsFalseMatcher IsFalse();
+
+ } // namespace testing
+
+ namespace absl_testing {
+ namespace status_internal {
+ class IsOkMatcher {};
+ template <typename M>
+ class IsOkAndHoldsMatcher {};
+ class StatusIsMatcher {};
+ class CanonicalStatusIsMatcher {};
+ } // namespace status_internal
+ status_internal::IsOkMatcher IsOk();
+ template <typename InnerMatcher>
+ status_internal::IsOkAndHoldsMatcher<InnerMatcher> IsOkAndHolds(
+ InnerMatcher m);
+ template <typename StatusCodeMatcher>
+ status_internal::StatusIsMatcher StatusIs(StatusCodeMatcher&& code_matcher);
+
+ template <typename StatusCodeMatcher>
+ status_internal::CanonicalStatusIsMatcher CanonicalStatusIs(
+ StatusCodeMatcher&& code_matcher);
+ } // namespace absl_testing
+
+ using testing::AssertionResult;
+#define EXPECT_TRUE(x) \
+ switch (0) \
+ case 0: \
+ default: \
+ if (const AssertionResult gtest_ar_ = AssertionResult(x)) { \
+ } else /* NOLINT */ \
+ ::testing::Message()
+#define EXPECT_FALSE(x) EXPECT_TRUE(!(x))
+
+#define GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ switch (0) \
+ case 0: \
+ default:
+
+#define GTEST_ASSERT_(expression, on_failure) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (const ::testing::AssertionResult gtest_ar = (expression)) \
+ ; \
+ else \
+ on_failure(gtest_ar.failure_message())
+#define GTEST_PRED_FORMAT1_(pred_format, v1, on_failure) \
+ GTEST_ASSERT_(pred_format(#v1, v1), on_failure)
+#define GTEST_PRED_FORMAT2_(pred_format, v1, v2, on_failure) \
+ GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2), on_failure)
+#define GTEST_MESSAGE_AT_(file, line, message, result_type) \
+ ::testing::internal::AssertHelper(result_type, file, line, message) = \
+ ::testing::Message()
+#define GTEST_MESSAGE_(message, result_type) \
+ GTEST_MESSAGE_AT_(__FILE__, __LINE__, message, result_type)
+#define GTEST_FATAL_FAILURE_(message) \
+ return GTEST_MESSAGE_(message, ::testing::TestPartResult::kFatalFailure)
+#define GTEST_NONFATAL_FAILURE_(message) \
+ GTEST_MESSAGE_(message, ::testing::TestPartResult::kNonFatalFailure)
+
+#define ASSERT_PRED_FORMAT1(pred_format, v1) \
+ GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_FATAL_FAILURE_)
+#define ASSERT_PRED_FORMAT2(pred_format, v1, v2) \
+ GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_FATAL_FAILURE_)
+
+#define ASSERT_THAT(value, matcher) \
+ ASSERT_PRED_FORMAT1( \
+ ::testing::internal::MakePredicateFormatterFromMatcher(matcher), \
+ value)
+#define ASSERT_OK(x) ASSERT_THAT(x, ::testing::status::IsOk())
+
+#define EXPECT_PRED_FORMAT1(pred_format, v1) \
+ GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_NONFATAL_FAILURE_)
+#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \
+ GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_NONFATAL_FAILURE_)
+#define EXPECT_THAT(value, matcher) \
+ EXPECT_PRED_FORMAT1( \
+ ::testing::internal::MakePredicateFormatterFromMatcher(matcher), \
+ value)
+#define EXPECT_OK(expression) EXPECT_THAT(expression, ::testing::status::IsOk())
+
+#define GTEST_TEST_BOOLEAN_(expression, text, actual, expected, fail) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (const ::testing::AssertionResult gtest_ar_ = \
+ ::testing::AssertionResult(expression)) \
+ ; \
+ else \
+ fail(::testing::internal::GetBoolAssertionFailureMessage( \
+ gtest_ar_, text, #actual, #expected) \
+ .c_str())
+#define GTEST_ASSERT_TRUE(condition) \
+ GTEST_TEST_BOOLEAN_(condition, #condition, false, true, GTEST_FATAL_FAILURE_)
+#define GTEST_ASSERT_FALSE(condition) \
+ GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \
+ GTEST_FATAL_FAILURE_)
+#define ASSERT_TRUE(condition) GTEST_ASSERT_TRUE(condition)
+#define ASSERT_FALSE(condition) GTEST_ASSERT_FALSE(condition)
+
+#define EXPECT_EQ(x, y) \
+ EXPECT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, x, y)
+#define EXPECT_NE(x, y) \
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperNE, x, y)
+#define EXPECT_LT(x, y) \
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLT, x, y)
+#define EXPECT_GT(x, y) \
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGT, x, y)
+#define EXPECT_LE(x, y) \
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLE, x, y)
+#define EXPECT_GE(x, y) \
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGE, x, y)
+
+#define ASSERT_EQ(x, y) \
+ ASSERT_PRED_FORMAT2(testing::internal::EqHelper::Compare, x, y)
+#define ASSERT_NE(x, y) \
+ ASSERT_PRED_FORMAT2(testing::internal::CmpHelperNE, x, y)
+#define ASSERT_LT(x, y) \
+ ASSERT_PRED_FORMAT2(testing::internal::CmpHelperLT, x, y)
+#define ASSERT_GT(x, y) \
+ ASSERT_PRED_FORMAT2(testing::internal::CmpHelperGT, x, y)
+#define ASSERT_LE(x, y) \
+ ASSERT_PRED_FORMAT2(testing::internal::CmpHelperLE, x, y)
+#define ASSERT_GE(x, y) \
+ ASSERT_PRED_FORMAT2(testing::internal::CmpHelperGE, x, y)
+
+#endif // BASE_DEFS_H_
+)cc";
+
+constexpr const char *kAbslTypeTraitsHeader = R"cc(
+#pragma clang system_header
+
+#ifndef ABSL_TYPE_TRAITS_H_
+#define ABSL_TYPE_TRAITS_H_
+
+#include "std_type_traits.h"
+
+ namespace absl {
+
+ template <typename... Ts>
+ struct conjunction : std::true_type {};
+
+ template <typename T, typename... Ts>
+ struct conjunction<T, Ts...>
+ : std::conditional<T::value, conjunction<Ts...>, T>::type {};
+
+ template <typename T>
+ struct conjunction<T> : T {};
+
+ template <typename... Ts>
+ struct disjunction : std::false_type {};
+
+ template <typename T, typename... Ts>
+ struct disjunction<T, Ts...>
+ : std::conditional<T::value, T, disjunction<Ts...>>::type {};
+
+ template <typename T>
+ struct disjunction<T> : T {};
+
+ template <typename T>
+ struct negation : std::integral_constant<bool, !T::value> {};
+
+ template <typename T>
+ using remove_cv_t = typename std::remove_cv<T>::type;
+
+ template <typename T>
+ using remove_reference_t = typename std::remove_reference<T>::type;
+
+ template <typename T>
+ using decay_t = typename std::decay<T>::type;
+
+ template <bool B, typename T = void>
+ using enable_if_t = typename std::enable_if<B, T>::type;
+
+ template <bool B, typename T, typename F>
+ using conditional_t = typename std::conditional<B, T, F>::type;
+
+ } // namespace absl
+
+#endif // ABSL_TYPE_TRAITS_H_
+)cc";
+
+constexpr const char *kStdTypeTraitsHeader = R"cc(
+#pragma clang system_header
+
+#ifndef STD_TYPE_TRAITS_H_
+#define STD_TYPE_TRAITS_H_
+
+#include "stdlib_defs.h"
+
+ namespace std {
+
+ template <bool B, class T, class F>
+ struct conditional {
+ typedef T type;
+ };
+
+ template <class T, class F>
+ struct conditional<false, T, F> {
+ typedef F type;
+ };
+
+ template <bool B, class T, class F>
+ using conditional_t = typename conditional<B, T, F>::type;
+
+ template <class...>
+ struct conjunction : true_type {};
+
+ template <class B1>
+ struct conjunction<B1> : B1 {};
+
+ template <class B1, class... Bn>
+ struct conjunction<B1, Bn...>
+ : conditional_t<bool(B1::value), conjunction<Bn...>, B1> {};
+
+ template <bool B, class T = void>
+ struct enable_if {};
+
+ template <class T>
+ struct enable_if<true, T> {
+ typedef T type;
+ };
+
+ template <bool B, class T = void>
+ using enable_if_t = typename enable_if<B, T>::type;
+
+ template <class T, class U>
+ struct is_same : false_type {};
+
+ template <class T>
+ struct is_same<T, T> : true_type {};
+
+ template <class T, class U>
+ inline constexpr bool is_same_v = is_same<T, U>::value;
+
+ template <class T>
+ struct is_array : false_type {};
+
+ template <class T>
+ struct is_array<T[]> : true_type {};
+
+ template <class T, size_t N>
+ struct is_array<T[N]> : true_type {};
+
+ template <class T>
+ struct remove_extent {
+ typedef T type;
+ };
+
+ template <class T>
+ struct remove_extent<T[]> {
+ typedef T type;
+ };
+
+ template <class T, size_t N>
+ struct remove_extent<T[N]> {
+ typedef T type;
+ };
+
+ // primary template
+ template <class>
+ struct is_function : false_type {};
+
+ // specialization for regular functions
+ template <class Ret, class... Args>
+ struct is_function<Ret(Args...)> : true_type {};
+
+ namespace detail {
+
+ template <class T>
+ struct type_identity {
+ using type = T;
+ }; // or use type_identity (since C++20)
+
+ template <class T>
+ auto try_add_pointer(int)
+ -> type_identity<typename remove_reference<T>::type*>;
+ template <class T>
+ auto try_add_pointer(...) -> type_identity<T>;
+
+ } // namespace detail
+
+ template <class T>
+ struct add_pointer : decltype(detail::try_add_pointer<T>(0)) {};
+
+ template <class T>
+ struct remove_cv {
+ typedef T type;
+ };
+ template <class T>
+ struct remove_cv<const T> {
+ typedef T type;
+ };
+ template <class T>
+ struct remove_cv<volatile T> {
+ typedef T type;
+ };
+ template <class T>
+ struct remove_cv<const volatile T> {
+ typedef T type;
+ };
+
+ template <class T>
+ struct remove_const {
+ typedef T type;
+ };
+ template <class T>
+ struct remove_const<const T> {
+ typedef T type;
+ };
+
+ template <class T>
+ struct remove_volatile {
+ typedef T type;
+ };
+ template <class T>
+ struct remove_volatile<volatile T> {
+ typedef T type;
+ };
+
+ template <class T>
+ using remove_cv_t = typename remove_cv<T>::type;
+
+ template <class T>
+ using remove_const_t = typename remove_const<T>::type;
+
+ template <class T>
+ using remove_volatile_t = typename remove_volatile<T>::type;
+
+ template <class T>
+ struct decay {
+ private:
+ typedef typename remove_reference<T>::type U;
+
+ public:
+ typedef typename conditional<
+ is_array<U>::value, typename remove_extent<U>::type*,
+ typename conditional<is_function<U>::value,
+ typename add_pointer<U>::type,
+ typename remove_cv<U>::type>::type>::type type;
+ };
+
+ namespace detail {
+
+ template <class T> // Note that `cv void&` is a substitution failure
+ auto try_add_lvalue_reference(int) -> type_identity<T&>;
+ template <class T> // Handle T = cv void case
+ auto try_add_lvalue_reference(...) -> type_identity<T>;
+
+ template <class T>
+ auto try_add_rvalue_reference(int) -> type_identity<T&&>;
+ template <class T>
+ auto try_add_rvalue_reference(...) -> type_identity<T>;
+
+ } // namespace detail
+
+ template <class T>
+ struct add_lvalue_reference
+ : decltype(detail::try_add_lvalue_reference<T>(0)) {};
+
+ template <class T>
+ struct add_rvalue_reference
+ : decltype(detail::try_add_rvalue_reference<T>(0)) {};
+
+ template <class T>
+ typename add_rvalue_reference<T>::type declval() noexcept;
+
+ template <class T>
+ struct is_void : is_same<void, typename remove_cv<T>::type> {};
+
+ namespace detail {
+
+ template <class T>
+ auto test_returnable(int)
+ -> decltype(void(static_cast<T (*)()>(nullptr)), true_type{});
+ template <class>
+ auto test_returnable(...) -> false_type;
+
+ template <class From, class To>
+ auto test_implicitly_convertible(int)
+ -> decltype(void(declval<void (&)(To)>()(declval<From>())), true_type{});
+ template <class, class>
+ auto test_implicitly_convertible(...) -> false_type;
+
+ } // namespace detail
+
+ template <class From, class To>
+ struct is_convertible
+ : integral_constant<
+ bool, (decltype(detail::test_returnable<To>(0))::value &&
+ decltype(detail::test_implicitly_convertible<From, To>(
+ 0))::value) ||
+ (is_void<From>::value && is_void<To>::value)> {};
+
+ template <class From, class To>
+ inline constexpr bool is_convertible_v = is_convertible<From, To>::value;
+
+ template <class...>
+ using void_t = void;
+
+ template <class, class T, class... Args>
+ struct is_constructible_ : false_type {};
+
+ template <class T, class... Args>
+ struct is_constructible_<void_t<decltype(T(declval<Args>()...))>, T, Args...>
+ : true_type {};
+
+ template <class T, class... Args>
+ using is_constructible = is_constructible_<void_t<>, T, Args...>;
+
+ template <class T, class... Args>
+ inline constexpr bool is_constructible_v =
+ is_constructible<T, Args...>::value;
+
+ template <class T>
+ struct is_reference : false_type {};
+ template <class T>
+ struct is_reference<T&> : true_type {};
+ template <class T>
+ struct is_reference<T&&> : true_type {};
+
+ template <class T>
+ inline constexpr bool is_reference_v = is_reference<T>::value;
+
+ template <class _Tp>
+ struct __uncvref {
+ typedef typename remove_cv<typename remove_reference<_Tp>::type>::type type;
+ };
+
+ template <class _Tp>
+ using __uncvref_t = typename __uncvref<_Tp>::type;
+
+ template <bool _Val>
+ using _BoolConstant = integral_constant<bool, _Val>;
+
+ template <class _Tp, class _Up>
+ using _IsSame = _BoolConstant<__is_same(_Tp, _Up)>;
+
+ template <class _Tp, class _Up>
+ using _IsNotSame = _BoolConstant<!__is_same(_Tp, _Up)>;
+
+ template <bool>
+ struct _MetaBase;
+ template <>
+ struct _MetaBase<true> {
+ template <class _Tp, class _Up>
+ using _SelectImpl = _Tp;
+ template <template <class...> class _FirstFn, template <class...> class,
+ class... _Args>
+ using _SelectApplyImpl = _FirstFn<_Args...>;
+ template <class _First, class...>
+ using _FirstImpl = _First;
+ template <class, class _Second, class...>
+ using _SecondImpl = _Second;
+ template <class _Result, class _First, class... _Rest>
+ using _OrImpl =
+ typename _MetaBase<_First::value != true && sizeof...(_Rest) != 0>::
+ template _OrImpl<_First, _Rest...>;
+ };
+
+ template <>
+ struct _MetaBase<false> {
+ template <class _Tp, class _Up>
+ using _SelectImpl = _Up;
+ template <template <class...> class, template <class...> class _SecondFn,
+ class... _Args>
+ using _SelectApplyImpl = _SecondFn<_Args...>;
+ template <class _Result, class...>
+ using _OrImpl = _Result;
+ };
+
+ template <bool _Cond, class _IfRes, class _ElseRes>
+ using _If = typename _MetaBase<_Cond>::template _SelectImpl<_IfRes, _ElseRes>;
+
+ template <class... _Rest>
+ using _Or = typename _MetaBase<sizeof...(_Rest) !=
+ 0>::template _OrImpl<false_type, _Rest...>;
+
+ template <bool _Bp, class _Tp = void>
+ using __enable_if_t = typename enable_if<_Bp, _Tp>::type;
+
+ template <class...>
+ using __expand_to_true = true_type;
+ template <class... _Pred>
+ __expand_to_true<__enable_if_t<_Pred::value>...> __and_helper(int);
+ template <class...>
+ false_type __and_helper(...);
+ template <class... _Pred>
+ using _And = decltype(__and_helper<_Pred...>(0));
+
+ template <class _Pred>
+ struct _Not : _BoolConstant<!_Pred::value> {};
+
+ struct __check_tuple_constructor_fail {
+ static constexpr bool __enable_explicit_default() { return false; }
+ static constexpr bool __enable_implicit_default() { return false; }
+ template <class...>
+ static constexpr bool __enable_explicit() {
+ return false;
+ }
+ template <class...>
+ static constexpr bool __enable_implicit() {
+ return false;
+ }
+ };
+
+ template <typename, typename _Tp>
+ struct __select_2nd {
+ typedef _Tp type;
+ };
+ template <class _Tp, class _Arg>
+ typename __select_2nd<decltype((declval<_Tp>() = declval<_Arg>())),
+ true_type>::type
+ __is_assignable_test(int);
+ template <class, class>
+ false_type __is_assignable_test(...);
+ template <class _Tp, class _Arg,
+ bool = is_void<_Tp>::value || is_void<_Arg>::value>
+ struct __is_assignable_imp
+ : public decltype((__is_assignable_test<_Tp, _Arg>(0))) {};
+ template <class _Tp, class _Arg>
+ struct __is_assignable_imp<_Tp, _Arg, true> : public false_type {};
+ template <class _Tp, class _Arg>
+ struct is_assignable : public __is_assignable_imp<_Tp, _Arg> {};
+
+ template <class _Tp>
+ struct __libcpp_is_integral : public false_type {};
+ template <>
+ struct __libcpp_is_integral<bool> : public true_type {};
+ template <>
+ struct __libcpp_is_integral<char> : public true_type {};
+ template <>
+ struct __libcpp_is_integral<signed char> : public true_type {};
+ template <>
+ struct __libcpp_is_integral<unsigned char> : public true_type {};
+ template <>
+ struct __libcpp_is_integral<wchar_t> : public true_type {};
+ template <>
+ struct __libcpp_is_integral<short> : public true_type {}; // NOLINT
+ template <>
+ struct __libcpp_is_integral<unsigned short> : public true_type {}; // NOLINT
+ template <>
+ struct __libcpp_is_integral<int> : public true_type {};
+ template <>
+ struct __libcpp_is_integral<unsigned int> : public true_type {};
+ template <>
+ struct __libcpp_is_integral<long> : public true_type {}; // NOLINT
+ template <>
+ struct __libcpp_is_integral<unsigned long> : public true_type {}; // NOLINT
+ template <>
+ struct __libcpp_is_integral<long long> : public true_type {}; // NOLINT
+ template <> // NOLINTNEXTLINE
+ struct __libcpp_is_integral<unsigned long long> : public true_type {};
+ template <class _Tp>
+ struct is_integral
+ : public __libcpp_is_integral<typename remove_cv<_Tp>::type> {};
+
+ template <class _Tp>
+ struct __libcpp_is_floating_point : public false_type {};
+ template <>
+ struct __libcpp_is_floating_point<float> : public true_type {};
+ template <>
+ struct __libcpp_is_floating_point<double> : public true_type {};
+ template <>
+ struct __libcpp_is_floating_point<long double> : public true_type {};
+ template <class _Tp>
+ struct is_floating_point
+ : public __libcpp_is_floating_point<typename remove_cv<_Tp>::type> {};
+
+ template <class _Tp>
+ struct is_arithmetic
+ : public integral_constant<bool, is_integral<_Tp>::value ||
+ is_floating_point<_Tp>::value> {};
+
+ template <class _Tp>
+ struct __libcpp_is_pointer : public false_type {};
+ template <class _Tp>
+ struct __libcpp_is_pointer<_Tp*> : public true_type {};
+ template <class _Tp>
+ struct is_pointer
+ : public __libcpp_is_pointer<typename remove_cv<_Tp>::type> {};
+
+ template <class _Tp>
+ struct __libcpp_is_member_pointer : public false_type {};
+ template <class _Tp, class _Up>
+ struct __libcpp_is_member_pointer<_Tp _Up::*> : public true_type {};
+ template <class _Tp>
+ struct is_member_pointer
+ : public __libcpp_is_member_pointer<typename remove_cv<_Tp>::type> {};
+
+ template <class _Tp>
+ struct __libcpp_union : public false_type {};
+ template <class _Tp>
+ struct is_union : public __libcpp_union<typename remove_cv<_Tp>::type> {};
+
+ struct __two {
+ char __lx[2];
+ };
+
+ namespace __is_class_imp {
+ template <class _Tp>
+ char __test(int _Tp::*);
+ template <class _Tp>
+ __two __test(...);
+ } // namespace __is_class_imp
+ template <class _Tp>
+ struct is_class
+ : public integral_constant<bool,
+ sizeof(__is_class_imp::__test<_Tp>(0)) == 1 &&
+ !is_union<_Tp>::value> {};
+
+ template <class _Tp>
+ struct __is_nullptr_t_impl : public false_type {};
+ template <>
+ struct __is_nullptr_t_impl<nullptr_t> : public true_type {};
+ template <class _Tp>
+ struct __is_nullptr_t
+ : public __is_nullptr_t_impl<typename remove_cv<_Tp>::type> {};
+ template <class _Tp>
+ struct is_null_pointer
+ : public __is_nullptr_t_impl<typename remove_cv<_Tp>::type> {};
+
+ template <class _Tp>
+ struct is_enum
+ : public integral_constant<
+ bool, !is_void<_Tp>::value && !is_integral<_Tp>::value &&
+ !is_floating_point<_Tp>::value && !is_array<_Tp>::value &&
+ !is_pointer<_Tp>::value && !is_reference<_Tp>::value &&
+ !is_member_pointer<_Tp>::value && !is_union<_Tp>::value &&
+ !is_class<_Tp>::value && !is_function<_Tp>::value> {};
+
+ template <class _Tp>
+ struct is_scalar
+ : public integral_constant<
+ bool, is_arithmetic<_Tp>::value || is_member_pointer<_Tp>::value ||
+ is_pointer<_Tp>::value || __is_nullptr_t<_Tp>::value ||
+ is_enum<_Tp>::value> {};
+ template <>
+ struct is_scalar<nullptr_t> : public true_type {};
+
+ } // namespace std
+
+#endif // STD_TYPE_TRAITS_H_
+)cc";
+
+constexpr const char *kStdLibDefsHeader = R"cc(
+#pragma clang system_header
+
+#ifndef STDLIB_DEFS_H_
+#define STDLIB_DEFS_H_
+
+ // mock definitions of entities in the C++ standard library.
+ //
+ // mocks are only defined to the extent needed. Feel free to extend their
+ // definitions to suit your new tests.
+ //
+ // If you find that you need to break up this file into smaller files then
+ // break along the boundaries of the stdlib's header files.
+
+ // From stddef.h
+ typedef decltype(sizeof(char)) size_t; // NOLINT
+ typedef decltype(sizeof(char*)) ptrdiff_t; // NOLINT
+
+ // From stdint.h
+ typedef __SIZE_TYPE__ size_t;
+ typedef __UINT8_TYPE__ uint8_t;
+ typedef __UINT16_TYPE__ uint16_t;
+ typedef __UINT32_TYPE__ uint32_t;
+ typedef __UINT64_TYPE__ uint64_t;
+ typedef __INT8_TYPE__ int8_t;
+ typedef __INT16_TYPE__ int16_t;
+ typedef __INT32_TYPE__ int32_t;
+ typedef __INT64_TYPE__ int64_t;
+
+ namespace std {
+ // Our implementation of the standard library puts everything inside namespace
+ // std into an inline namespace, whose name should not be hardcoded in
+ // ClangTidy.
+ inline namespace __do_not_hardcode_in_clang_tidy {
+
+ using size_t = ::size_t;
+
+ template <typename T, T V>
+ struct integral_constant {
+ static constexpr T value = V;
+ };
+ using true_type = integral_constant<bool, true>;
+ using false_type = integral_constant<bool, false>;
+
+ using nullptr_t = decltype(nullptr);
+
+ template <typename T>
+ struct _NonDeducedImpl {
+ using type = T;
+ };
+ template <typename T>
+ using _NonDeduced = typename _NonDeducedImpl<T>::type;
+
+ // clang-format off
+template <typename T> struct remove_reference { using type = T; };
+template <typename T> struct remove_reference<T&> { using type = T; };
+template <typename T> struct remove_reference<T&&> { using type = T; };
+ // clang-format on
+
+ template <typename T>
+ using remove_reference_t = typename remove_reference<T>::type;
+
+ template <typename T>
+ constexpr T&& forward(remove_reference_t<T>& t) noexcept;
+
+ template <typename T>
+ constexpr T&& forward(remove_reference_t<T>&& t) noexcept;
+
+ template <typename T>
+ void swap(T& a1, T& a2);
+
+ template <typename F>
+ class function {
+ public:
+ function();
+ function(const function&);
+ function(function&&);
+ function& operator=(const function&);
+ function& operator=(function&&);
+ ~function();
+ };
+
+ template <typename T>
+ class initializer_list {
+ public:
+ using size_type = decltype(sizeof(0));
+ const T* begin() const;
+ const T* end() const;
+
+ private:
+ constexpr initializer_list(const T* items, size_type size)
+ : items_(items), size_(size) {}
+ const T* items_;
+ size_type size_;
+ };
+
+ // <functional>
+ template <class T>
+ struct equal_to {
+ bool operator()(const T& lhs, const T& rhs) const;
+ };
+
+ template <class T>
+ struct not_equal_to {
+ bool operator()(const T& lhs, const T& rhs) const;
+ };
+
+ template <class T>
+ struct hash {
+ std::size_t operator()(T const& t) const noexcept;
+ };
+
+ template <class T = void>
+ struct less {
+ constexpr bool operator()(const T& lhs, const T& rhs) const;
+ };
+
+ template <>
+ struct less<void> {
+ using is_transparent = void;
+ template <typename T, typename U>
+ constexpr auto operator()(T&& lhs, U&& rhs) const
+ -> decltype(std::forward<T>(lhs) < std::forward<U>(rhs));
+ };
+
+ template <class T>
+ struct greater {
+ bool operator()(const T& lhs, const T& rhs) const;
+ };
+
+ // <algorithm>
+ template <class T>
+ const T& min(const T& a, const T& b);
+ template <class T, class Compare>
+ const T& min(const T& a, const T& b, Compare comp);
+ template <class T>
+ const T& max(const T& a, const T& b);
+ template <class T, class Compare>
+ const T& max(const T& a, const T& b, Compare comp);
+ template <class T>
+ T min(std::initializer_list<T> ilist);
+ template <class T, class Compare>
+ T min(std::initializer_list<T> ilist, Compare comp);
+ template <class T>
+ T max(std::initializer_list<T> ilist);
+ template <class T, class Compare>
+ T max(std::initializer_list<T> ilist, Compare comp);
+
+ // <memory>
+ template <class T>
+ struct allocator {
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ typedef T* pointer;
+ typedef const T* const_pointer;
+ typedef T value_type;
+
+ T* allocate(size_t n);
+ };
+
+ template <class Alloc>
+ struct allocator_traits {
+ typedef Alloc allocator_type;
+ typedef typename allocator_type::value_type value_type;
+ typedef typename allocator_type::pointer pointer;
+ typedef typename allocator_type::const_pointer const_pointer;
+ typedef typename allocator_type::difference_type difference_type;
+ typedef typename allocator_type::size_type size_type;
+ };
+
+ template <class... T>
+ class tuple {};
+
+ template <std::size_t I, class T>
+ struct tuple_element;
+
+ template <std::size_t I, class T, class... Ts>
+ struct tuple_element<I, std::tuple<T, Ts...>>
+ : std::tuple_element<I - 1, std::tuple<Ts...>> {};
+
+ template <class T, class... Ts>
+ struct tuple_element<0, std::tuple<T, Ts...>> {
+ using type = T;
+ };
+
+ template <std::size_t I, typename T>
+ using tuple_element_t = typename tuple_element<I, T>::type;
+
+ template <class T1, class T2>
+ struct pair {
+ T1 first;
+ T2 second;
+
+ typedef T1 first_type;
+ typedef T2 second_type;
+
+ constexpr pair();
+
+ template <class U1, class U2>
+ pair(pair<U1, U2>&& p);
+
+ template <class U1, class U2>
+ pair(U1&& x, U2&& y);
+ };
+
+ template <class T1, class T2>
+ pair<T1, T2> make_pair(T1&& t1, T2&& t2);
+
+ template <class InputIt1, class InputIt2, class OutputIt>
+ OutputIt set_difference(InputIt1 first1, InputIt1 last1, InputIt2 first2,
+ InputIt2 last2, OutputIt d_first);
+
+ template <class InputIt1, class InputIt2, class OutputIt>
+ OutputIt set_intersection(InputIt1 first1, InputIt1 last1, InputIt2 first2,
+ InputIt2 last2, OutputIt d_first);
+
+ template <class InputIt1, class InputIt2, class OutputIt>
+ OutputIt set_union(InputIt1 first1, InputIt1 last1, InputIt2 first2,
+ InputIt2 last2, OutputIt d_first);
+
+ template <class InputIt1, class InputIt2, class OutputIt>
+ OutputIt set_symmetric_difference(InputIt1 first1, InputIt1 last1,
+ InputIt2 first2, InputIt2 last2,
+ OutputIt d_first);
+
+ template <class InputIt1, class InputIt2, class OutputIt>
+ OutputIt merge(InputIt1 first1, InputIt1 last1, InputIt2 first2,
+ InputIt2 last2, OutputIt d_first);
+
+ template <class Key, class T, class Compare = less<Key>,
+ class Allocator = allocator<pair<const Key, T>>>
+ class map {
+ public:
+ map();
+ ~map();
+
+ typedef pair<const Key, T> value_type;
+ struct iterator {
+ struct reference {};
+ reference operator*() const;
+ struct pointer {};
+ pointer operator->() const;
+
+ friend bool operator==(const iterator& lhs, const iterator& rhs);
+ friend bool operator!=(const iterator& lhs, const iterator& rhs);
+ iterator& operator++();
+ iterator operator++(int);
+ };
+ struct const_iterator {
+ struct reference {};
+ reference operator*() const;
+ struct pointer {};
+ pointer operator->() const;
+
+ friend bool operator==(const const_iterator& lhs,
+ const const_iterator& rhs);
+ friend bool operator!=(const const_iterator& lhs,
+ const const_iterator& rhs);
+ const_iterator& operator++();
+ const_iterator operator++(int);
+ };
+
+ iterator begin();
+ iterator end();
+ const_iterator begin() const;
+ const_iterator end() const;
+
+ bool empty() const;
+ iterator find(const Key& key);
+ const_iterator find(const Key& key) const;
+ T& operator[](const Key& key);
+ T& at(const Key& key);
+ const T& at(const Key& key) const;
+ pair<iterator, bool> insert(const value_type& value);
+ template <class P>
+ pair<iterator, bool> insert(P&& value);
+ size_t count(const Key& key) const;
+ };
+
+ // <unordered_map>
+ template <class Key, class T, class Hash = hash<Key>,
+ class Pred = equal_to<Key>,
+ class Alloc = allocator<pair<const Key, T>>>
+ class unordered_map {
+ public:
+ // types
+ typedef Key key_type;
+ typedef T mapped_type;
+ typedef Hash hasher;
+ typedef Pred key_equal;
+ typedef Alloc allocator_type;
+ typedef pair<const key_type, mapped_type> value_type;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+ typedef typename allocator_traits<allocator_type>::pointer pointer;
+ typedef
+ typename allocator_traits<allocator_type>::const_pointer const_pointer;
+ typedef typename allocator_traits<allocator_type>::size_type size_type;
+ typedef typename allocator_traits<allocator_type>::difference_type
+ difference_type;
+
+ struct iterator {
+ struct reference {};
+ reference operator*() const;
+ struct pointer {};
+ pointer operator->() const;
+
+ friend bool operator==(const iterator& lhs, const iterator& rhs);
+ friend bool operator!=(const iterator& lhs, const iterator& rhs);
+ iterator& operator++();
+ iterator operator++(int);
+ };
+ struct const_iterator {
+ struct reference {};
+ reference operator*() const;
+ struct pointer {};
+ pointer operator->() const;
+
+ friend bool operator==(const const_iterator& lhs,
+ const const_iterator& rhs);
+ friend bool operator!=(const const_iterator& lhs,
+ const const_iterator& rhs);
+ const_iterator& operator++();
+ const_iterator operator++(int);
+ };
+
+ unordered_map() noexcept;
+ explicit unordered_map(size_type n, const hasher& hf = hasher(),
+ const key_equal& eql = key_equal(),
+ const allocator_type& a = allocator_type());
+ template <class InputIterator>
+ unordered_map(InputIterator f, InputIterator l, size_type n = 0,
+ const hasher& hf = hasher(),
+ const key_equal& eql = key_equal(),
+ const allocator_type& a = allocator_type());
+ explicit unordered_map(const allocator_type&);
+ unordered_map(const unordered_map&);
+ unordered_map(const unordered_map&, const Alloc&);
+ unordered_map(unordered_map&&) noexcept;
+ unordered_map(unordered_map&&, const Alloc&);
+ ~unordered_map();
+ unordered_map& operator=(const unordered_map&);
+
+ allocator_type get_allocator() const noexcept;
+
+ bool empty() const noexcept;
+ size_type size() const noexcept;
+ size_type max_size() const noexcept;
+
+ iterator begin() noexcept;
+ iterator end() noexcept;
+ const_iterator begin() const noexcept;
+ const_iterator end() const noexcept;
+ const_iterator cbegin() const noexcept;
+ const_iterator cend() const noexcept;
+
+ hasher hash_function() const;
+ key_equal key_eq() const;
+
+ iterator find(const key_type& k);
+ const_iterator find(const key_type& k) const;
+ size_type count(const key_type& k) const;
+ bool contains(const key_type& k) const; // C++20
+ pair<iterator, iterator> equal_range(const key_type& k);
+ pair<const_iterator, const_iterator> equal_range(const key_type& k) const;
+
+ mapped_type& operator[](const key_type& k);
+ mapped_type& operator[](key_type&& k);
+
+ mapped_type& at(const key_type& k);
+ const mapped_type& at(const key_type& k) const;
+ };
+
+ template <class Key, class Compare = less<Key>,
+ class Allocator = allocator<Key>>
+ class set {
+ public:
+ typedef Key key_type;
+ typedef key_type value_type;
+ typedef Compare key_compare;
+ typedef key_compare value_compare;
+
+ set();
+ set(initializer_list<value_type> l, const value_compare& c = value_compare());
+ ~set();
+
+ struct iterator {
+ Key& operator*();
+ friend bool operator==(const iterator& lhs, const iterator& rhs);
+ friend bool operator!=(const iterator& lhs, const iterator& rhs);
+ iterator& operator++();
+ };
+ struct const_iterator {
+ Key& operator*() const;
+ friend bool operator==(const const_iterator& lhs,
+ const const_iterator& rhs);
+ friend bool operator!=(const const_iterator& lhs,
+ const const_iterator& rhs);
+ const_iterator& operator++();
+ };
+
+ iterator begin();
+ iterator end();
+ const_iterator begin() const;
+ const_iterator end() const;
+
+ bool empty() const;
+ iterator find(const key_type& key);
+ const_iterator find(const key_type& key) const;
+ pair<iterator, bool> insert(const value_type& value);
+ size_t count(const key_type& key) const;
+ };
+
+ template <class Key, class Hash = hash<Key>, class KeyEqual = equal_to<Key>,
+ class Allocator = allocator<Key>>
+ class unordered_set {
+ public:
+ using key_type = Key;
+ using value_type = Key;
+ using size_type = typename allocator_traits<Allocator>::size_type;
+ using difference_type = typename allocator_traits<Allocator>::difference_type;
+ using hasher = Hash;
+ using key_equal = KeyEqual;
+ using allocator_type = Allocator;
+ using reference = value_type&;
+ using const_reference = const value_type&;
+ using pointer = typename allocator_traits<Allocator>::pointer;
+ using const_pointer = typename allocator_traits<Allocator>::const_pointer;
+
+ struct iterator {
+ reference operator*() const;
+ pointer operator->() const;
+
+ friend bool operator==(const iterator& lhs, const iterator& rhs);
+ friend bool operator!=(const iterator& lhs, const iterator& rhs);
+ iterator& operator++();
+ iterator operator++(int);
+ };
+ struct const_iterator {
+ const_reference operator*() const;
+ const_pointer operator->() const;
+
+ friend bool operator==(const const_iterator& lhs,
+ const const_iterator& rhs);
+ friend bool operator!=(const const_iterator& lhs,
+ const const_iterator& rhs);
+ const_iterator& operator++();
+ const_iterator operator++(int);
+ };
+
+ unordered_set() noexcept;
+ explicit unordered_set(size_type bucket_count, const Hash& hash = Hash(),
+ const key_equal& equal = key_equal(),
+ const Allocator& alloc = Allocator());
+ unordered_set(size_type bucket_count, const Allocator& alloc);
+ unordered_set(size_type bucket_count, const Hash& hash,
+ const Allocator& alloc);
+ explicit unordered_set(const Allocator& alloc);
+ template <class InputIt>
+ unordered_set(InputIt first, InputIt last, size_type bucket_count = 1,
+ const Hash& hash = Hash(),
+ const key_equal& equal = key_equal(),
+ const Allocator& alloc = Allocator());
+ template <class InputIt>
+ unordered_set(InputIt first, InputIt last, size_type bucket_count,
+ const Allocator& alloc);
+ template <class InputIt>
+ unordered_set(InputIt first, InputIt last, size_type bucket_count,
+ const Hash& hash, const Allocator& alloc);
+ unordered_set(const unordered_set& other);
+ unordered_set(const unordered_set& other, const Allocator& alloc);
+ unordered_set(unordered_set&& other);
+ unordered_set(unordered_set&& other, const Allocator& alloc);
+ unordered_set(initializer_list<value_type> init, size_type bucket_count = 1,
+ const Hash& hash = Hash(),
+ const key_equal& equal = key_equal(),
+ const Allocator& alloc = Allocator());
+ unordered_set(initializer_list<value_type> init, size_type bucket_count,
+ const Allocator& alloc);
+ unordered_set(initializer_list<value_type> init, size_type bucket_count,
+ const Hash& hash, const Allocator& alloc);
+
+ ~unordered_set();
+
+ unordered_set& operator=(const unordered_set& other);
+ unordered_set& operator=(unordered_set&& other);
+ unordered_set& operator=(initializer_list<value_type> ilist);
+
+ iterator begin() noexcept;
+ const_iterator begin() const noexcept;
+ const_iterator cbegin() const noexcept;
+ iterator end() noexcept;
+ const_iterator end() const noexcept;
+ const_iterator cend() const noexcept;
+
+ bool empty() const noexcept;
+ size_type size() const noexcept;
+
+ void clear() noexcept;
+
+ std::pair<iterator, bool> insert(const value_type& value);
+ std::pair<iterator, bool> insert(value_type&& value);
+ iterator insert(const_iterator hint, const value_type& value);
+ iterator insert(const_iterator hint, value_type&& value);
+ template <class InputIt>
+ void insert(InputIt first, InputIt last);
+ void insert(initializer_list<value_type> ilist);
+
+ template <class... Args>
+ pair<iterator, bool> emplace(Args&&... args);
+
+ iterator erase(const_iterator pos);
+ iterator erase(const_iterator first, const_iterator last);
+ size_type erase(const key_type& key);
+
+ void swap(unordered_set& other);
+
+ size_type count(const Key& key) const;
+ iterator find(const Key& key);
+ const_iterator find(const Key& key) const;
+ };
+
+ template <typename T, class Allocator = allocator<T>>
+ class vector {
+ public:
+ using value_type = T;
+ using size_type = typename allocator_traits<Allocator>::size_type;
+
+ // Constructors.
+ vector() {}
+ vector(size_type, const Allocator& = Allocator()) {}
+ vector(initializer_list<T> initializer_list,
+ const Allocator& = Allocator()) {}
+ vector(const vector& vector) {}
+ ~vector();
+
+ // Modifiers.
+ void push_back(const T& value);
+ void push_back(T&& value);
+ template <typename... Args>
+ T& emplace_back(Args&&... args);
+
+ // Iterators
+ class InputIterator {
+ public:
+ InputIterator(const InputIterator&);
+ ~InputIterator();
+ InputIterator& operator=(const InputIterator&);
+ InputIterator& operator++();
+ T& operator*() const;
+ bool operator!=(const InputIterator&) const;
+ bool operator==(const InputIterator&) const;
+ };
+ typedef InputIterator iterator;
+ typedef const InputIterator const_iterator;
+ iterator begin() noexcept;
+ const_iterator begin() const noexcept;
+ const_iterator cbegin() const noexcept;
+ iterator end() noexcept;
+ const_iterator end() const noexcept;
+ const_iterator cend() const noexcept;
+ T* data() noexcept;
+ const T* data() const noexcept;
+ T& operator[](int n);
+ const T& operator[](int n) const;
+ T& at(int n);
+ const T& at(int n) const;
+ size_t size() const;
+ };
+
+ template <typename T, class Allocator = allocator<T>>
+ class deque {
+ public:
+ using value_type = T;
+ using size_type = typename allocator_traits<Allocator>::size_type;
+
+ // Constructors.
+ deque() {}
+ deque(size_type, const Allocator& = Allocator()) {}
+ deque(initializer_list<T> initializer_list, const Allocator& = Allocator()) {}
+ deque(const deque& deque) {}
+ ~deque();
+
+ // Modifiers.
+ void push_back(const T& value);
+ void push_back(T&& value);
+ void push_front(const T& value);
+ void push_front(T&& value);
+ template <typename... Args>
+ T& emplace_back(Args&&... args);
+ template <typename... Args>
+ T& emplace_front(Args&&... args);
+
+ // Iterators
+ class InputIterator {
+ public:
+ InputIterator(const InputIterator&);
+ ~InputIterator();
+ InputIterator& operator=(const InputIterator&);
+ InputIterator& operator++();
+ T& operator*() const;
+ bool operator!=(const InputIterator&) const;
+ bool operator==(const InputIterator&) const;
+ };
+ typedef InputIterator iterator;
+ typedef const InputIterator const_iterator;
+ iterator begin() noexcept;
+ const_iterator begin() const noexcept;
+ iterator end() noexcept;
+ const_iterator end() const noexcept;
+ T* data() noexcept;
+ const T* data() const noexcept;
+ T& operator[](size_t n);
+ const T& operator[](size_t n) const;
+ T& at(size_t n);
+ const T& at(size_t n) const;
+ size_t size() const;
+ };
+
+ template <class T, size_t N>
+ struct array {
+ using value_type = T;
+ void fill(const T& value);
+ void swap(array& other) noexcept;
+ size_t size() const noexcept;
+ size_t max_size() const noexcept;
+ bool empty() const noexcept;
+ T& operator[](size_t n) noexcept;
+ const T& operator[](size_t n) const noexcept;
+ T& at(size_t n);
+ const T& at(size_t n) const;
+ T& front() noexcept;
+ const T& front() const noexcept;
+ T& back() noexcept;
+ const T& back() const noexcept;
+ T* data() noexcept;
+ const T* data() const noexcept;
+ };
+
+ template <typename T>
+ struct default_delete {};
+
+ template <typename T, typename D = default_delete<T>>
+ class unique_ptr {
+ public:
+ using element_type = T;
+ using deleter_type = D;
+
+ constexpr unique_ptr();
+ constexpr unique_ptr(nullptr_t) noexcept;
+ unique_ptr(unique_ptr&&);
+ explicit unique_ptr(T*);
+ template <typename U, typename E>
+ unique_ptr(unique_ptr<U, E>&&);
+
+ ~unique_ptr();
+
+ unique_ptr& operator=(unique_ptr&&);
+ template <typename U, typename E>
+ unique_ptr& operator=(unique_ptr<U, E>&&);
+ unique_ptr& operator=(nullptr_t);
+
+ void reset(T* = nullptr) noexcept;
+ T* release();
+ T* get() const;
+
+ T& operator*() const;
+ T* operator->() const;
+ explicit operator bool() const noexcept;
+ };
+
+ template <typename T, typename D>
+ class unique_ptr<T[], D> {
+ public:
+ T* get() const;
+ T& operator[](size_t i);
+ const T& operator[](size_t i) const;
+ };
+
+ template <typename T, typename... Args>
+ unique_ptr<T> make_unique(Args&&...);
+
+ template <class T, class D>
+ void swap(unique_ptr<T, D>& x, unique_ptr<T, D>& y) noexcept;
+
+ template <class T1, class D1, class T2, class D2>
+ bool operator==(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);
+ template <class T1, class D1, class T2, class D2>
+ bool operator!=(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);
+ template <class T1, class D1, class T2, class D2>
+ bool operator<(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);
+ template <class T1, class D1, class T2, class D2>
+ bool operator<=(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);
+ template <class T1, class D1, class T2, class D2>
+ bool operator>(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);
+ template <class T1, class D1, class T2, class D2>
+ bool operator>=(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);
+
+ template <class T, class D>
+ bool operator==(const unique_ptr<T, D>& x, nullptr_t) noexcept;
+ template <class T, class D>
+ bool operator==(nullptr_t, const unique_ptr<T, D>& y) noexcept;
+ template <class T, class D>
+ bool operator!=(const unique_ptr<T, D>& x, nullptr_t) noexcept;
+ template <class T, class D>
+ bool operator!=(nullptr_t, const unique_ptr<T, D>& y) noexcept;
+ template <class T, class D>
+ bool operator<(const unique_ptr<T, D>& x, nullptr_t);
+ template <class T, class D>
+ bool operator<(nullptr_t, const unique_ptr<T, D>& y);
+ template <class T, class D>
+ bool operator<=(const unique_ptr<T, D>& x, nullptr_t);
+ template <class T, class D>
+ bool operator<=(nullptr_t, const unique_ptr<T, D>& y);
+ template <class T, class D>
+ bool operator>(const unique_ptr<T, D>& x, nullptr_t);
+ template <class T, class D>
+ bool operator>(nullptr_t, const unique_ptr<T, D>& y);
+ template <class T, class D>
+ bool operator>=(const unique_ptr<T, D>& x, nullptr_t);
+ template <class T, class D>
+ bool operator>=(nullptr_t, const unique_ptr<T, D>& y);
+
+ template <typename T>
+ class shared_ptr_base {
+ public:
+ ~shared_ptr_base();
+ T& operator*() const;
+ T* operator->() const;
+ };
+
+ template <typename T>
+ class shared_ptr : public shared_ptr_base<T> {
+ public:
+ constexpr shared_ptr();
+ ~shared_ptr();
+ };
+
+ template <class T, class... Args>
+ shared_ptr<T> make_shared(Args&&... args);
+
+ template <typename T>
+ class weak_ptr {
+ public:
+ constexpr weak_ptr() noexcept;
+ ~weak_ptr();
+ };
+
+ template <typename T>
+ class reference_wrapper {
+ public:
+ reference_wrapper(T& ref);
+ T& get() const noexcept;
+ operator T&() const noexcept;
+ };
+
+ template <typename Container>
+ typename Container::iterator begin(Container&);
+ template <typename Container>
+ typename Container::iterator end(Container&);
+
+ template <class T>
+ reference_wrapper<const T> cref(const T& t);
+
+ template <typename T>
+ constexpr std::remove_reference_t<T>&& move(T&& x);
+
+ struct nothrow_t {
+ explicit nothrow_t() = default;
+ };
+ extern const std::nothrow_t nothrow;
+
+ } // namespace __do_not_hardcode_in_clang_tidy
+ } // namespace std
+
+#endif // STDLIB_DEFS_H_
+)cc";
+
+constexpr const char *kStringDefsHeader = R"cc(
+ // string_defs.h
+ //
+ // This file contains minimal mock declarations of the string type and
+ // friends. It can be used in tests to use matchers as they would be used in a
+ // prod google3 environment. This only contains what has been necessary for
+ // the tests that use it. It can be expanded if tests need more of it.
+
+#pragma clang system_header
+
+#ifndef STRING_DEFS_H_
+#define STRING_DEFS_H_
+
+#include "std_char_traits.h"
+#include "std_string_view.h"
+#include "std_type_traits.h"
+#include "stdlib_defs.h"
+
+#define ABSL_PRINTF_ATTRIBUTE(a, b) __attribute__((format(printf, a, b)))
+
+ namespace __gnu_cxx {
+ inline namespace __do_not_hardcode_in_clang_tidy {
+ template <typename A, typename B, typename C>
+ class __string_base {};
+ } // namespace __do_not_hardcode_in_clang_tidy
+ } // namespace __gnu_cxx
+
+ namespace std {
+ inline namespace __do_not_hardcode_in_clang_tidy {
+
+ template <class _CharT, class _Traits, class _Tp>
+ struct __can_be_converted_to_string_view {
+ static std::true_type Test(std::basic_string_view<_CharT, _Traits>);
+ static std::false_type Test(...);
+ static const _Tp& Instance();
+ static constexpr bool value = decltype(Test(Instance()))::value;
+ };
+
+ template <typename A, typename B = std::char_traits<A>,
+ typename C = std::allocator<A>>
+ class basic_string : public __gnu_cxx::__string_base<A, B, C> {
+ public:
+ basic_string();
+ basic_string(const basic_string&);
+ basic_string(basic_string&&) noexcept;
+ basic_string(const A*, const C& = C());
+ basic_string(const A*, int, const C& = C());
+ basic_string(const basic_string&, int, int, C = C());
+
+ template <class _Tp, class = enable_if_t<__can_be_converted_to_string_view<
+ A, B, _Tp>::value>>
+ explicit basic_string(const _Tp& __t);
+
+ template <class _Tp, class = enable_if_t<__can_be_converted_to_string_view<
+ A, B, _Tp>::value>>
+ explicit basic_string(const _Tp& __t, const C& __a);
+
+ ~basic_string();
+
+ basic_string& operator=(const basic_string&);
+ basic_string& operator=(basic_string&&) noexcept;
+ basic_string& operator=(const A*);
+ template <class _Tp, class = enable_if_t<__can_be_converted_to_string_view<
+ A, B, _Tp>::value>>
+ basic_string& operator=(const _Tp& __t);
+
+ basic_string& operator+=(const basic_string&);
+
+ static const int npos = -1;
+ const char* c_str() const;
+ const char* data() const;
+ char* data();
+ size_t size() const;
+ size_t capacity() const;
+ size_t length() const;
+ bool empty() const;
+ char& operator[](int);
+ const char& operator[](int) const;
+ char& at(int);
+ const char& at(int) const;
+ void clear();
+ void resize(int);
+ int compare(const basic_string&) const;
+ int find(const basic_string&, int pos = 0) const;
+ int find(const char*, int pos = 0) const;
+ int rfind(const basic_string&, int pos = 0) const;
+ int rfind(const char*, int pos = 0) const;
+
+ operator std::basic_string_view<A, B>() const noexcept;
+ };
+
+ template <typename A, typename B, typename C>
+ basic_string<A, B, C> operator+(const basic_string<A, B, C>&,
+ const basic_string<A, B, C>&);
+ template <typename A, typename B, typename C>
+ basic_string<A, B, C> operator+(const basic_string<A, B, C>&, const char*);
+
+ typedef basic_string<char> string;
+
+ bool operator==(const string&, const string&);
+ bool operator==(const string&, const char*);
+ bool operator==(const char*, const string&);
+
+ bool operator!=(const string&, const string&);
+ bool operator<(const string&, const string&);
+ bool operator>(const string&, const string&);
+ bool operator<=(const string&, const string&);
+ bool operator>=(const string&, const string&);
+ } // namespace __do_not_hardcode_in_clang_tidy
+ } // namespace std
+
+#endif // STRING_DEFS_H_
+)cc";
+
+constexpr const char *kStdCharTraitsHeader = R"cc(
+#ifndef STD_CHAR_TRAITS_H_
+#define STD_CHAR_TRAITS_H_
+
+ namespace std {
+ inline namespace __do_not_hardcode_in_clang_tidy {
+ template <typename T>
+ class char_traits {};
+ } // namespace __do_not_hardcode_in_clang_tidy
+ } // namespace std
+
+#endif // STD_CHAR_TRAITS_H_
+)cc";
+
+constexpr const char *kStdStringViewHeader = R"cc(
+ // This file is a mock of the <string_view> standard library header.
+
+#pragma clang system_header
+
+#ifndef STD_STRING_VIEW_H_
+#define STD_STRING_VIEW_H_
+
+#include "std_char_traits.h"
+#include "stdlib_defs.h"
+
+ constexpr size_t strlen(const char* p) {
+ return *p ? strlen(p + 1) + 1 : 0;
+ }
+
+ namespace std {
+ inline namespace __do_not_hardcode_in_clang_tidy {
+
+ template <typename CharT, typename Traits = std::char_traits<CharT>>
+ class basic_string_view {
+ public:
+ using traits_type = Traits;
+
+ constexpr basic_string_view() noexcept;
+ constexpr basic_string_view(const CharT* null_terminated_string) { // NOLINT
+ (void)strlen(null_terminated_string); // some checks need to see strlen.
+ }
+ constexpr basic_string_view(const CharT*, size_t);
+
+#if __cplusplus >= 202002L
+ // A subset of:
+ // template <contiguous_iterator _It, sized_sentinel_for<_It> _End>
+ // requires(is_same_v<iter_value_t<_It>, _CharT> &&
+ // !is_convertible_v<_End, size_type>)
+ // constexpr basic_string_view(_It __begin, _End __end);
+ // to avoid having to define all the underlying concepts, etc.
+ constexpr basic_string_view(const CharT* __begin, const CharT* __end);
+#endif
+
+ constexpr const char* data() const noexcept;
+ constexpr size_t size() const noexcept;
+ constexpr size_t length() const noexcept;
+ constexpr const CharT& operator[](size_t pos) const;
+ constexpr const CharT& at(size_t pos) const;
+ };
+
+ using string_view = basic_string_view<char>;
+
+ template <class CharT, class Traits>
+ constexpr bool operator==(basic_string_view<CharT, Traits> lhs,
+ basic_string_view<CharT, Traits> rhs) noexcept;
+ template <class CharT, class Traits>
+ constexpr bool operator==(_NonDeduced<basic_string_view<CharT, Traits>> lhs,
+ basic_string_view<CharT, Traits> rhs) noexcept;
+ template <class CharT, class Traits>
+ constexpr bool operator==(
+ basic_string_view<CharT, Traits> lhs,
+ _NonDeduced<basic_string_view<CharT, Traits>> rhs) noexcept;
+
+ } // namespace __do_not_hardcode_in_clang_tidy
+ } // namespace std
+
+#endif // STD_STRING_VIEW_H_
+)cc";
+
+constexpr const char *kStatusOrDefsHeader =
+ R"cc(
+#ifndef STATUSOR_H_
+#define STATUSOR_H_
+
+#include "absl_defs.h"
+#include "std_string_view.h"
+#include "stdlib_defs.h"
+#include "string_defs.h"
+
+ namespace absl {
+ template <typename>
+ class AnyInvocable;
+ template <typename R, typename... A>
+ class AnyInvocable<R(A...) &&> {
+ public:
+ AnyInvocable();
+ ~AnyInvocable();
+ template <typename F>
+ AnyInvocable(F&& f, decltype(f(A{}...), 0) = 0);
+
+ R operator()(A...) &&;
+ };
+ } // namespace absl
+
+ namespace absl {
+ struct SourceLocation {
+ static constexpr SourceLocation current();
+ static constexpr SourceLocation DoNotInvokeDirectlyNoSeriouslyDont(
+ int line, const char* file_name);
+ };
+ } // namespace absl
+ namespace absl {
+ enum class StatusCode : int {
+ kOk,
+ kCancelled,
+ kUnknown,
+ kInvalidArgument,
+ kDeadlineExceeded,
+ kNotFound,
+ kAlreadyExists,
+ kPermissionDenied,
+ kResourceExhausted,
+ kFailedPrecondition,
+ kAborted,
+ kOutOfRange,
+ kUnimplemented,
+ kInternal,
+ kUnavailable,
+ kDataLoss,
+ kUnauthenticated,
+ };
+ } // namespace absl
+
+ namespace absl {
+ enum class StatusToStringMode : int {
+ kWithNoExtraData = 0,
+ kWithPayload = 1 << 0,
+ kWithSourceLocation = 1 << 1,
+ kWithEverything = ~kWithNoExtraData,
+ kDefault = kWithPayload,
+ };
+ class Status {
+ public:
+ Status();
+ template <typename Enum>
+ Status(Enum code, std::string_view msg);
+ Status(absl::StatusCode code, std::string_view msg,
+ absl::SourceLocation loc = SourceLocation::current());
+ Status(const Status& base_status, absl::SourceLocation loc);
+ Status(Status&& base_status, absl::SourceLocation loc);
+ ~Status() {}
+
+ Status(const Status&);
+ Status& operator=(const Status& x);
+
+ Status(Status&&) noexcept;
+ Status& operator=(Status&&);
+
+ friend bool operator==(const Status&, const Status&);
+ friend bool operator!=(const Status&, const Status&);
+
+ bool ok() const { return true; }
+ void CheckSuccess() const;
+ void IgnoreError() const;
+ int error_code() const;
+ absl::Status ToCanonical() const;
+ std::string ToString(
+ StatusToStringMode m = StatusToStringMode::kDefault) const;
+ void Update(const Status& new_status);
+ void Update(Status&& new_status);
+
+ private:
+ void TestToStringSimplification() const {
+ LOG(INFO) << this->ToString() << ToString();
+ }
+ };
+
+ bool operator==(const Status& lhs, const Status& rhs);
+ bool operator!=(const Status& lhs, const Status& rhs);
+
+ Status OkStatus();
+ Status InvalidArgumentError(char*);
+
+ template <typename T>
+ struct StatusOr;
+
+ namespace internal_statusor {
+
+ template <typename T, typename U, typename = void>
+ struct HasConversionOperatorToStatusOr : std::false_type {};
+
+ template <typename T, typename U>
+ void test(char (*)[sizeof(std::declval<U>().operator absl::StatusOr<T>())]);
+
+ template <typename T, typename U>
+ struct HasConversionOperatorToStatusOr<T, U, decltype(test<T, U>(0))>
+ : std::true_type {};
+
+ template <typename T, typename U>
+ using IsConstructibleOrConvertibleFromStatusOr =
+ absl::disjunction<std::is_constructible<T, StatusOr<U>&>,
+ std::is_constructible<T, const StatusOr<U>&>,
+ std::is_constructible<T, StatusOr<U>&&>,
+ std::is_constructible<T, const StatusOr<U>&&>,
+ std::is_convertible<StatusOr<U>&, T>,
+ std::is_convertible<const StatusOr<U>&, T>,
+ std::is_convertible<StatusOr<U>&&, T>,
+ std::is_convertible<const StatusOr<U>&&, T>>;
+
+ template <typename T, typename U>
+ using IsConstructibleOrConvertibleOrAssignableFromStatusOr =
+ absl::disjunction<IsConstructibleOrConvertibleFromStatusOr<T, U>,
+ std::is_assignable<T&, StatusOr<U>&>,
+ std::is_assignable<T&, const StatusOr<U>&>,
+ std::is_assignable<T&, StatusOr<U>&&>,
+ std::is_assignable<T&, const StatusOr<U>&&>>;
+
+ template <typename T, typename U>
+ struct IsDirectInitializationAmbiguous
+ : public absl::conditional_t<
+ std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>,
+ U>::value,
+ std::false_type,
+ IsDirectInitializationAmbiguous<
+ T, absl::remove_cv_t<absl::remove_reference_t<U>>>> {};
+
+ template <typename T, typename V>
+ struct IsDirectInitializationAmbiguous<T, absl::StatusOr<V>>
+ : public IsConstructibleOrConvertibleFromStatusOr<T, V> {};
+
+ template <typename T, typename U>
+ using IsDirectInitializationValid = absl::disjunction<
+ // Short circuits if T is basically U.
+ std::is_same<T, absl::remove_cv_t<absl::remove_reference_t<U>>>,
+ absl::negation<absl::disjunction<
+ std::is_same<absl::StatusOr<T>,
+ absl::remove_cv_t<absl::remove_reference_t<U>>>,
+ std::is_same<absl::Status,
+ absl::remove_cv_t<absl::remove_reference_t<U>>>,
+ std::is_same<absl::in_place_t,
+ absl::remove_cv_t<absl::remove_reference_t<U>>>,
+ IsDirectInitializationAmbiguous<T, U>>>>;
+
+ template <typename T, typename U>
+ struct IsForwardingAssignmentAmbiguous
+ : public absl::conditional_t<
+ std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>,
+ U>::value,
+ std::false_type,
+ IsForwardingAssignmentAmbiguous<
+ T, absl::remove_cv_t<absl::remove_reference_t<U>>>> {};
+
+ template <typename T, typename U>
+ struct IsForwardingAssignmentAmbiguous<T, absl::StatusOr<U>>
+ : public IsConstructibleOrConvertibleOrAssignableFromStatusOr<T, U> {};
+
+ template <typename T, typename U>
+ using IsForwardingAssignmentValid = absl::disjunction<
+ // Short circuits if T is basically U.
+ std::is_same<T, absl::remove_cv_t<absl::remove_reference_t<U>>>,
+ absl::negation<absl::disjunction<
+ std::is_same<absl::StatusOr<T>,
+ absl::remove_cv_t<absl::remove_reference_t<U>>>,
+ std::is_same<absl::Status,
+ absl::remove_cv_t<absl::remove_reference_t<U>>>,
+ std::is_same<absl::in_place_t,
+ absl::remove_cv_t<absl::remove_reference_t<U>>>,
+ IsForwardingAssignmentAmbiguous<T, U>>>>;
+
+ template <typename T, typename U>
+ using IsForwardingAssignmentValid = absl::disjunction<
+ // Short circuits if T is basically U.
+ std::is_same<T, absl::remove_cv_t<absl::remove_reference_t<U>>>,
+ absl::negation<absl::disjunction<
+ std::is_same<absl::StatusOr<T>,
+ absl::remove_cv_t<absl::remove_reference_t<U>>>,
+ std::is_same<absl::Status,
+ absl::remove_cv_t<absl::remove_reference_t<U>>>,
+ std::is_same<absl::in_place_t,
+ absl::remove_cv_t<absl::remove_reference_t<U>>>,
+ IsForwardingAssignmentAmbiguous<T, U>>>>;
+
+ template <typename T>
+ struct OperatorBase {
+ const T& value() const&;
+ T& value() &;
+ const T&& value() const&&;
+ T&& value() &&;
+
+ const T& operator*() const&;
+ T& operator*() &;
+ const T&& operator*() const&&;
+ T&& operator*() &&;
+
+ // To test that analyses are okay if there is a use of operator*
+ // within this base class.
+ const T* operator->() const { return __builtin_addressof(**this); }
+ T* operator->() { return __builtin_addressof(**this); }
+ };
+
+ } // namespace internal_statusor
+
+ template <typename T>
+ struct StatusOr : private internal_statusor::OperatorBase<T> {
+ explicit StatusOr();
+
+ StatusOr(const StatusOr&) = default;
+ StatusOr& operator=(const StatusOr&) = default;
+
+ StatusOr(StatusOr&&) = default;
+ StatusOr& operator=(StatusOr&&) = default;
+
+ template <
+ typename U,
+ absl::enable_if_t<
+ absl::conjunction<
+ absl::negation<std::is_same<T, U>>,
+ std::is_constructible<T, const U&>,
+ std::is_convertible<const U&, T>,
+ absl::negation<
+ internal_statusor::IsConstructibleOrConvertibleFromStatusOr<
+ T, U>>>::value,
+ int> = 0>
+ StatusOr(const StatusOr<U>&);
+
+ template <
+ typename U,
+ absl::enable_if_t<
+ absl::conjunction<
+ absl::negation<std::is_same<T, U>>,
+ std::is_constructible<T, const U&>,
+ absl::negation<std::is_convertible<const U&, T>>,
+ absl::negation<
+ internal_statusor::IsConstructibleOrConvertibleFromStatusOr<
+ T, U>>>::value,
+ int> = 0>
+ explicit StatusOr(const StatusOr<U>&);
+
+ template <
+ typename U,
+ absl::enable_if_t<
+ absl::conjunction<
+ absl::negation<std::is_same<T, U>>,
+ std::is_constructible<T, U&&>, std::is_convertible<U&&, T>,
+ absl::negation<
+ internal_statusor::IsConstructibleOrConvertibleFromStatusOr<
+ T, U>>>::value,
+ int> = 0>
+ StatusOr(StatusOr<U>&&);
+
+ template <
+ typename U,
+ absl::enable_if_t<
+ absl::conjunction<
+ absl::negation<std::is_same<T, U>>,
+ std::is_constructible<T, U&&>,
+ absl::negation<std::is_convertible<U&&, T>>,
+ absl::negation<
+ internal_statusor::IsConstructibleOrConvertibleFromStatusOr<
+ T, U>>>::value,
+ int> = 0>
+ explicit StatusOr(StatusOr<U>&&);
+
+ template <
+ typename U,
+ absl::enable_if_t<
+ absl::conjunction<
+ absl::negation<std::is_same<T, U>>,
+ std::is_constructible<T, const U&>,
+ std::is_assignable<T, const U&>,
+ absl::negation<
+ internal_statusor::
+ IsConstructibleOrConvertibleOrAssignableFromStatusOr<
+ T, U>>>::value,
+ int> = 0>
+ StatusOr& operator=(const StatusOr<U>&);
+
+ template <
+ typename U,
+ absl::enable_if_t<
+ absl::conjunction<
+ absl::negation<std::is_same<T, U>>,
+ std::is_constructible<T, U&&>, std::is_assignable<T, U&&>,
+ absl::negation<
+ internal_statusor::
+ IsConstructibleOrConvertibleOrAssignableFromStatusOr<
+ T, U>>>::value,
+ int> = 0>
+ StatusOr& operator=(StatusOr<U>&&);
+
+ template <typename U = absl::Status,
+ absl::enable_if_t<
+ absl::conjunction<
+ std::is_convertible<U&&, absl::Status>,
+ std::is_constructible<absl::Status, U&&>,
+ absl::negation<
+ std::is_same<absl::decay_t<U>, absl::StatusOr<T>>>,
+ absl::negation<std::is_same<absl::decay_t<U>, T>>,
+ absl::negation<
+ std::is_same<absl::decay_t<U>, absl::in_place_t>>,
+ absl::negation<
+ internal_statusor::HasConversionOperatorToStatusOr<
+ T, U&&>>>::value,
+ int> = 0>
+ StatusOr(U&&);
+
+ template <typename U = absl::Status,
+ absl::enable_if_t<
+ absl::conjunction<
+ absl::negation<std::is_convertible<U&&, absl::Status>>,
+ std::is_constructible<absl::Status, U&&>,
+ absl::negation<
+ std::is_same<absl::decay_t<U>, absl::StatusOr<T>>>,
+ absl::negation<std::is_same<absl::decay_t<U>, T>>,
+ absl::negation<
+ std::is_same<absl::decay_t<U>, absl::in_place_t>>,
+ absl::negation<
+ internal_statusor::HasConversionOperatorToStatusOr<
+ T, U&&>>>::value,
+ int> = 0>
+ explicit StatusOr(U&&);
+
+ template <typename U = absl::Status,
+ absl::enable_if_t<
+ absl::conjunction<
+ std::is_convertible<U&&, absl::Status>,
+ std::is_constructible<absl::Status, U&&>,
+ absl::negation<
+ std::is_same<absl::decay_t<U>, absl::StatusOr<T>>>,
+ absl::negation<std::is_same<absl::decay_t<U>, T>>,
+ absl::negation<
+ std::is_same<absl::decay_t<U>, absl::in_place_t>>,
+ absl::negation<
+ internal_statusor::HasConversionOperatorToStatusOr<
+ T, U&&>>>::value,
+ int> = 0>
+ StatusOr& operator=(U&&);
+
+ template <
+ typename U = T,
+ typename = typename std::enable_if<absl::conjunction<
+ std::is_constructible<T, U&&>, std::is_assignable<T&, U&&>,
+ absl::disjunction<
+ std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>, T>,
+ absl::conjunction<
+ absl::negation<std::is_convertible<U&&, absl::Status>>,
+ absl::negation<
+ internal_statusor::HasConversionOperatorToStatusOr<
+ T, U&&>>>>,
+ internal_statusor::IsForwardingAssignmentValid<T, U&&>>::value>::
+ type>
+ StatusOr& operator=(U&&);
+
+ template <typename... Args>
+ explicit StatusOr(absl::in_place_t, Args&&...);
+
+ template <typename U, typename... Args>
+ explicit StatusOr(absl::in_place_t, std::initializer_list<U>, Args&&...);
+
+ template <
+ typename U = T,
+ absl::enable_if_t<
+ absl::conjunction<
+ internal_statusor::IsDirectInitializationValid<T, U&&>,
+ std::is_constructible<T, U&&>, std::is_convertible<U&&, T>,
+ absl::disjunction<
+ std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>,
+ T>,
+ absl::conjunction<
+ absl::negation<std::is_convertible<U&&, absl::Status>>,
+ absl::negation<
+ internal_statusor::HasConversionOperatorToStatusOr<
+ T, U&&>>>>>::value,
+ int> = 0>
+ StatusOr(U&&);
+
+ template <
+ typename U = T,
+ absl::enable_if_t<
+ absl::conjunction<
+ internal_statusor::IsDirectInitializationValid<T, U&&>,
+ absl::disjunction<
+ std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>,
+ T>,
+ absl::conjunction<
+ absl::negation<
+ std::is_constructible<absl::Status, U&&>>,
+ absl::negation<
+ internal_statusor::HasConversionOperatorToStatusOr<
+ T, U&&>>>>,
+ std::is_constructible<T, U&&>,
+ absl::negation<std::is_convertible<U&&, T>>>::value,
+ int> = 0>
+ explicit StatusOr(U&&);
+
+ bool ok() const;
+
+ const Status& status() const& { return status_; }
+ Status status() &&;
+
+ using StatusOr::OperatorBase::value;
+
+ const T& ValueOrDie() const&;
+ T& ValueOrDie() &;
+ const T&& ValueOrDie() const&&;
+ T&& ValueOrDie() &&;
+
+ using StatusOr::OperatorBase::operator*;
+
+ using StatusOr::OperatorBase::operator->;
+
+ template <typename U>
+ T value_or(U&& default_value) const&;
+ template <typename U>
+ T value_or(U&& default_value) &&;
+
+ template <typename... Args>
+ T& emplace(Args&&... args);
+
+ template <
+ typename U, typename... Args,
+ absl::enable_if_t<std::is_constructible<T, std::initializer_list<U>&,
+ Args&&...>::value,
+ int> = 0>
+ T& emplace(std::initializer_list<U> ilist, Args&&... args);
+
+ private:
+ absl::Status status_;
+ };
+
+ template <typename T>
+ bool operator==(const StatusOr<T>& lhs, const StatusOr<T>& rhs);
+
+ template <typename T>
+ bool operator!=(const StatusOr<T>& lhs, const StatusOr<T>& rhs);
+
+ } // namespace absl
+
+#endif // STATUSOR_H_
+ )cc";
+
+TEST_P(UncheckedStatusOrAccessModelTest, NoStatusOrMention) {
+ ExpectDiagnosticsFor(R"cc(
+ void target() { "nop"; }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest,
+ UnwrapWithoutCheck_Lvalue_CallToValue) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, NonExplicitInitialization) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_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_use_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest,
+ UnwrapWithoutCheck_Rvalue_CallToValue) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_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_use_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ sor.ValueOrDie(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest,
+ UnwrapWithoutCheck_Rvalue_CallToValueOrDie) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_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_use_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ *sor; // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest,
+ UnwrapWithoutCheck_Lvalue_CallToOperatorStarSeparateLine) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ * // [[unsafe]]
+ sor;
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest,
+ UnwrapWithoutCheck_Rvalue_CallToOperatorStar) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ if (!sor.ok()) return;
+
+ sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ if (sor.ok()) return;
+
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_use_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_use_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ if (sor.ok()) {
+ } else {
+ return;
+ }
+
+ sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_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_use_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_use_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_use_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_use_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_use_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_use_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ while (Make<bool>()) sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ while (sor.ok()) sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ while (!sor.ok()) sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ while (!!sor.ok()) sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ while (sor.ok()) {
+ }
+
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_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_use_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ while (!sor.ok()) return;
+
+ sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ while (sor.ok()) return;
+
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_TerminatingBranch_Continue) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ while (Make<bool>()) {
+ if (!sor.ok()) continue;
+
+ sor.value();
+ }
+
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_NestedIfWithBinaryCondition) {
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_test_defs.h"
+
+ void target(STATUSOR_BOOL sor) {
+ bool b = sor.ok() & sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_test_defs.h"
+
+ void target(STATUSOR_BOOL sor) {
+ bool b = sor.ok() && sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_test_defs.h"
+
+ void target(STATUSOR_BOOL sor) {
+ bool b = !sor.ok() && sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_test_defs.h"
+
+ void target(STATUSOR_BOOL sor) {
+ bool b = sor.ok() || sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_test_defs.h"
+
+ void target(STATUSOR_BOOL sor) {
+ bool b = !sor.ok() || sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_test_defs.h"
+
+ void f();
+
+ void target(STATUSOR_INT sor) {
+ if (!sor.ok()) f();
+
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_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_use_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_use_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_use_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_use_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_use_test_defs.h"
+
+ void terminate() __attribute__((noreturn));
+
+ void target(STATUSOR_INT sor) {
+ sor.value(); // [[unsafe]]
+ terminate();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_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_use_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_use_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ while (auto ok = sor.ok()) sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_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_use_test_defs.h"
+
+ void target() {
+ while (Make<bool>()) {
+ STATUSOR_INT sor = Make<STATUSOR_INT>();
+ sor.value(); // [[unsafe]]
+ }
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_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_use_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_use_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_use_test_defs.h"
+
+ bool unknown();
+
+ void target(STATUSOR_INT sor) {
+ if (unknown() || sor.ok()) sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_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_use_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_use_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_use_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_use_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_use_test_defs.h"
+
+ void target() {
+ if (STATUSOR_INT sor = Make<STATUSOR_INT>(); sor.ok()) sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_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_use_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ bool b = false;
+ if (b) sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_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_use_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ sor.ok() ? sor.value() : Fatal().value();
+
+ sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ !sor.ok() ? Fatal().value() : sor.value();
+
+ sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_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_use_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_use_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_use_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_use_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ CHECK(sor.ok());
+ sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_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_use_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ QCHECK(sor.ok());
+ sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_test_defs.h"
+
+ void target(STATUSOR_INT& sor) {
+ if (sor.ok())
+ sor.value();
+ else
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ [&]() {
+ if (sor.ok())
+ sor.value();
+ else
+ sor.value(); // [[unsafe]]
+ }();
+ }
+ )cc");
+ ExpectDiagnosticsForLambda(R"cc(
+#include "unchecked_statusor_use_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ [=]() {
+ if (sor.ok())
+ sor.value();
+ else
+ sor.value(); // [[unsafe]]
+ }();
+ }
+ )cc");
+ ExpectDiagnosticsForLambda(R"cc(
+#include "unchecked_statusor_use_test_defs.h"
+
+ struct Foo {
+ STATUSOR_INT sor;
+
+ void target() {
+ [this]() {
+ if (sor.ok())
+ sor.value();
+ else
+ sor.value(); // [[unsafe]]
+ }();
+ }
+ };
+ )cc");
+}
+
+// TODO(sgatev): Ensure the following test cases are covered.
+TEST_P(UncheckedStatusOrAccessModelTest, Status) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_test_defs.h"
+
+ void foo();
+
+ void target(STATUS s) {
+ if (s.ok()) foo();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_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_use_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ EXPECT_THAT(sor, testing::status::IsOk());
+
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_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_use_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_use_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ EXPECT_OK(sor);
+
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ EXPECT_OK(sor.status());
+
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_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_use_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, UsingDeclaration) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_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_use_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_use_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ label:
+ if (!sor.ok()) goto label;
+ sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_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_use_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_use_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_use_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, VarDeclInitExprWithoutValue) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_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_use_test_defs.h"
+
+ class Bar;
+
+ class Foo : public absl::StatusOr<Bar> {};
+
+ void target(Foo opt) {
+ opt.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, SubclassStatus) {
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_use_test_defs.h"
+
+ class Foo : public STATUS {};
+
+ void target(Foo opt) { opt.ok(); }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, SubclassOk) {
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_use_test_defs.h"
+
+ class Bar;
+
+ class Foo : public absl::StatusOr<Bar> {};
+
+ void target(Foo opt) {
+ if (opt.ok()) opt.value();
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, SubclassOperator) {
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_use_test_defs.h"
+
+ class Bar;
+
+ class Foo : public absl::StatusOr<Bar> {};
+
+ void target(Foo opt) {
+ *opt; // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, GoodLambda) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_use_test_defs.h"
+
+ int target() {
+ STATUSOR_INT sor = Make<STATUSOR_INT>();
+ if (sor.ok()) return [&s = sor.value()] { return s; }();
+ return 0;
+ }
+ )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) {
+ std::vector<std::pair<std::string, std::string>> Headers;
+ Headers.emplace_back("absl_type_traits.h", kAbslTypeTraitsHeader);
+ Headers.emplace_back("absl_defs.h", kAbslDefsHeader);
+ Headers.emplace_back("std_type_traits.h", kStdTypeTraitsHeader);
+ Headers.emplace_back("std_string_view.h", kStdStringViewHeader);
+ Headers.emplace_back("std_char_traits.h", kStdCharTraitsHeader);
+ Headers.emplace_back("string_defs.h", kStringDefsHeader);
+ Headers.emplace_back("statusor_defs.h", kStatusOrDefsHeader);
+ Headers.emplace_back("stdlib_defs.h", kStdLibDefsHeader);
+ Headers.emplace_back("unchecked_statusor_use_test_defs.h",
+ R"cc(
+#include "absl_defs.h"
+#include "statusor_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..19505eb7740be
--- /dev/null
+++ b/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.h
@@ -0,0 +1,159 @@
+//===- 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
+ // TODO(sgatev): Investigate why we need to specify the operator name to
+ // avoid matching the lambda destructor here and not in other similar tests.
+ 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 1afd342f67ce4..d4e4db24a57ec 100644
--- a/llvm/utils/gn/secondary/clang/unittests/Analysis/FlowSensitive/BUILD.gn
+++ b/llvm/utils/gn/secondary/clang/unittests/Analysis/FlowSensitive/BUILD.gn
@@ -43,6 +43,8 @@ unittest("ClangAnalysisFlowSensitiveTests") {
"TransferTest.cpp",
"TypeErasedDataflowAnalysisTest.cpp",
"UncheckedOptionalAccessModelTest.cpp",
+ "UncheckedStatusOrAccessModelTest.cpp",
+ "UncheckedStatusOrAccessModelTestFixture.cpp",
"ValueTest.cpp",
"WatchedLiteralsSolverTest.cpp",
]
>From 21442747b6a79cb84b4b831c43992517b357d981 Mon Sep 17 00:00:00 2001
From: Florian Mayer <fmayer at google.com>
Date: Fri, 10 Oct 2025 15:16:55 -0700
Subject: [PATCH 2/3] nit
Created using spr 1.3.4
---
.../FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp | 1 -
.../FlowSensitive/UncheckedStatusOrAccessModelTestFixture.h | 2 --
2 files changed, 3 deletions(-)
diff --git a/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp b/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp
index af30e98c51c56..6d9e7614f9260 100644
--- a/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp
@@ -4697,7 +4697,6 @@ TEST_P(UncheckedStatusOrAccessModelTest, Lambdas) {
)cc");
}
-// TODO(sgatev): Ensure the following test cases are covered.
TEST_P(UncheckedStatusOrAccessModelTest, Status) {
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_use_test_defs.h"
diff --git a/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.h b/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.h
index 19505eb7740be..ff1d323a0086f 100644
--- a/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.h
+++ b/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.h
@@ -85,8 +85,6 @@ class UncheckedStatusOrAccessModelTestExecutor
std::string SourceCode,
UncheckedStatusOrAccessModelTestAliasKind AliasKind) const override {
using namespace ::clang::ast_matchers; // NOLINT: Too many names
- // TODO(sgatev): Investigate why we need to specify the operator name to
- // avoid matching the lambda destructor here and not in other similar tests.
ExpectDiagnosticsFor(SourceCode,
allOf(hasOverloadedOperatorName("()"),
hasDeclContext(cxxRecordDecl(isLambda()))),
>From 2d827ee73550c63762d6d78cdb40e942ef202a1c Mon Sep 17 00:00:00 2001
From: Florian Mayer <fmayer at google.com>
Date: Fri, 10 Oct 2025 16:33:34 -0700
Subject: [PATCH 3/3] remove some mock headers
Created using spr 1.3.4
---
...ncheckedStatusOrAccessModelTestFixture.cpp | 362 ------------------
1 file changed, 362 deletions(-)
diff --git a/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp b/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp
index 6d9e7614f9260..c9c655481d271 100644
--- a/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp
@@ -1504,299 +1504,6 @@ template <typename T> struct remove_reference<T&&> { using type = T; };
OutputIt merge(InputIt1 first1, InputIt1 last1, InputIt2 first2,
InputIt2 last2, OutputIt d_first);
- template <class Key, class T, class Compare = less<Key>,
- class Allocator = allocator<pair<const Key, T>>>
- class map {
- public:
- map();
- ~map();
-
- typedef pair<const Key, T> value_type;
- struct iterator {
- struct reference {};
- reference operator*() const;
- struct pointer {};
- pointer operator->() const;
-
- friend bool operator==(const iterator& lhs, const iterator& rhs);
- friend bool operator!=(const iterator& lhs, const iterator& rhs);
- iterator& operator++();
- iterator operator++(int);
- };
- struct const_iterator {
- struct reference {};
- reference operator*() const;
- struct pointer {};
- pointer operator->() const;
-
- friend bool operator==(const const_iterator& lhs,
- const const_iterator& rhs);
- friend bool operator!=(const const_iterator& lhs,
- const const_iterator& rhs);
- const_iterator& operator++();
- const_iterator operator++(int);
- };
-
- iterator begin();
- iterator end();
- const_iterator begin() const;
- const_iterator end() const;
-
- bool empty() const;
- iterator find(const Key& key);
- const_iterator find(const Key& key) const;
- T& operator[](const Key& key);
- T& at(const Key& key);
- const T& at(const Key& key) const;
- pair<iterator, bool> insert(const value_type& value);
- template <class P>
- pair<iterator, bool> insert(P&& value);
- size_t count(const Key& key) const;
- };
-
- // <unordered_map>
- template <class Key, class T, class Hash = hash<Key>,
- class Pred = equal_to<Key>,
- class Alloc = allocator<pair<const Key, T>>>
- class unordered_map {
- public:
- // types
- typedef Key key_type;
- typedef T mapped_type;
- typedef Hash hasher;
- typedef Pred key_equal;
- typedef Alloc allocator_type;
- typedef pair<const key_type, mapped_type> value_type;
- typedef value_type& reference;
- typedef const value_type& const_reference;
- typedef typename allocator_traits<allocator_type>::pointer pointer;
- typedef
- typename allocator_traits<allocator_type>::const_pointer const_pointer;
- typedef typename allocator_traits<allocator_type>::size_type size_type;
- typedef typename allocator_traits<allocator_type>::difference_type
- difference_type;
-
- struct iterator {
- struct reference {};
- reference operator*() const;
- struct pointer {};
- pointer operator->() const;
-
- friend bool operator==(const iterator& lhs, const iterator& rhs);
- friend bool operator!=(const iterator& lhs, const iterator& rhs);
- iterator& operator++();
- iterator operator++(int);
- };
- struct const_iterator {
- struct reference {};
- reference operator*() const;
- struct pointer {};
- pointer operator->() const;
-
- friend bool operator==(const const_iterator& lhs,
- const const_iterator& rhs);
- friend bool operator!=(const const_iterator& lhs,
- const const_iterator& rhs);
- const_iterator& operator++();
- const_iterator operator++(int);
- };
-
- unordered_map() noexcept;
- explicit unordered_map(size_type n, const hasher& hf = hasher(),
- const key_equal& eql = key_equal(),
- const allocator_type& a = allocator_type());
- template <class InputIterator>
- unordered_map(InputIterator f, InputIterator l, size_type n = 0,
- const hasher& hf = hasher(),
- const key_equal& eql = key_equal(),
- const allocator_type& a = allocator_type());
- explicit unordered_map(const allocator_type&);
- unordered_map(const unordered_map&);
- unordered_map(const unordered_map&, const Alloc&);
- unordered_map(unordered_map&&) noexcept;
- unordered_map(unordered_map&&, const Alloc&);
- ~unordered_map();
- unordered_map& operator=(const unordered_map&);
-
- allocator_type get_allocator() const noexcept;
-
- bool empty() const noexcept;
- size_type size() const noexcept;
- size_type max_size() const noexcept;
-
- iterator begin() noexcept;
- iterator end() noexcept;
- const_iterator begin() const noexcept;
- const_iterator end() const noexcept;
- const_iterator cbegin() const noexcept;
- const_iterator cend() const noexcept;
-
- hasher hash_function() const;
- key_equal key_eq() const;
-
- iterator find(const key_type& k);
- const_iterator find(const key_type& k) const;
- size_type count(const key_type& k) const;
- bool contains(const key_type& k) const; // C++20
- pair<iterator, iterator> equal_range(const key_type& k);
- pair<const_iterator, const_iterator> equal_range(const key_type& k) const;
-
- mapped_type& operator[](const key_type& k);
- mapped_type& operator[](key_type&& k);
-
- mapped_type& at(const key_type& k);
- const mapped_type& at(const key_type& k) const;
- };
-
- template <class Key, class Compare = less<Key>,
- class Allocator = allocator<Key>>
- class set {
- public:
- typedef Key key_type;
- typedef key_type value_type;
- typedef Compare key_compare;
- typedef key_compare value_compare;
-
- set();
- set(initializer_list<value_type> l, const value_compare& c = value_compare());
- ~set();
-
- struct iterator {
- Key& operator*();
- friend bool operator==(const iterator& lhs, const iterator& rhs);
- friend bool operator!=(const iterator& lhs, const iterator& rhs);
- iterator& operator++();
- };
- struct const_iterator {
- Key& operator*() const;
- friend bool operator==(const const_iterator& lhs,
- const const_iterator& rhs);
- friend bool operator!=(const const_iterator& lhs,
- const const_iterator& rhs);
- const_iterator& operator++();
- };
-
- iterator begin();
- iterator end();
- const_iterator begin() const;
- const_iterator end() const;
-
- bool empty() const;
- iterator find(const key_type& key);
- const_iterator find(const key_type& key) const;
- pair<iterator, bool> insert(const value_type& value);
- size_t count(const key_type& key) const;
- };
-
- template <class Key, class Hash = hash<Key>, class KeyEqual = equal_to<Key>,
- class Allocator = allocator<Key>>
- class unordered_set {
- public:
- using key_type = Key;
- using value_type = Key;
- using size_type = typename allocator_traits<Allocator>::size_type;
- using difference_type = typename allocator_traits<Allocator>::difference_type;
- using hasher = Hash;
- using key_equal = KeyEqual;
- using allocator_type = Allocator;
- using reference = value_type&;
- using const_reference = const value_type&;
- using pointer = typename allocator_traits<Allocator>::pointer;
- using const_pointer = typename allocator_traits<Allocator>::const_pointer;
-
- struct iterator {
- reference operator*() const;
- pointer operator->() const;
-
- friend bool operator==(const iterator& lhs, const iterator& rhs);
- friend bool operator!=(const iterator& lhs, const iterator& rhs);
- iterator& operator++();
- iterator operator++(int);
- };
- struct const_iterator {
- const_reference operator*() const;
- const_pointer operator->() const;
-
- friend bool operator==(const const_iterator& lhs,
- const const_iterator& rhs);
- friend bool operator!=(const const_iterator& lhs,
- const const_iterator& rhs);
- const_iterator& operator++();
- const_iterator operator++(int);
- };
-
- unordered_set() noexcept;
- explicit unordered_set(size_type bucket_count, const Hash& hash = Hash(),
- const key_equal& equal = key_equal(),
- const Allocator& alloc = Allocator());
- unordered_set(size_type bucket_count, const Allocator& alloc);
- unordered_set(size_type bucket_count, const Hash& hash,
- const Allocator& alloc);
- explicit unordered_set(const Allocator& alloc);
- template <class InputIt>
- unordered_set(InputIt first, InputIt last, size_type bucket_count = 1,
- const Hash& hash = Hash(),
- const key_equal& equal = key_equal(),
- const Allocator& alloc = Allocator());
- template <class InputIt>
- unordered_set(InputIt first, InputIt last, size_type bucket_count,
- const Allocator& alloc);
- template <class InputIt>
- unordered_set(InputIt first, InputIt last, size_type bucket_count,
- const Hash& hash, const Allocator& alloc);
- unordered_set(const unordered_set& other);
- unordered_set(const unordered_set& other, const Allocator& alloc);
- unordered_set(unordered_set&& other);
- unordered_set(unordered_set&& other, const Allocator& alloc);
- unordered_set(initializer_list<value_type> init, size_type bucket_count = 1,
- const Hash& hash = Hash(),
- const key_equal& equal = key_equal(),
- const Allocator& alloc = Allocator());
- unordered_set(initializer_list<value_type> init, size_type bucket_count,
- const Allocator& alloc);
- unordered_set(initializer_list<value_type> init, size_type bucket_count,
- const Hash& hash, const Allocator& alloc);
-
- ~unordered_set();
-
- unordered_set& operator=(const unordered_set& other);
- unordered_set& operator=(unordered_set&& other);
- unordered_set& operator=(initializer_list<value_type> ilist);
-
- iterator begin() noexcept;
- const_iterator begin() const noexcept;
- const_iterator cbegin() const noexcept;
- iterator end() noexcept;
- const_iterator end() const noexcept;
- const_iterator cend() const noexcept;
-
- bool empty() const noexcept;
- size_type size() const noexcept;
-
- void clear() noexcept;
-
- std::pair<iterator, bool> insert(const value_type& value);
- std::pair<iterator, bool> insert(value_type&& value);
- iterator insert(const_iterator hint, const value_type& value);
- iterator insert(const_iterator hint, value_type&& value);
- template <class InputIt>
- void insert(InputIt first, InputIt last);
- void insert(initializer_list<value_type> ilist);
-
- template <class... Args>
- pair<iterator, bool> emplace(Args&&... args);
-
- iterator erase(const_iterator pos);
- iterator erase(const_iterator first, const_iterator last);
- size_type erase(const key_type& key);
-
- void swap(unordered_set& other);
-
- size_type count(const Key& key) const;
- iterator find(const Key& key);
- const_iterator find(const Key& key) const;
- };
-
template <typename T, class Allocator = allocator<T>>
class vector {
public:
@@ -1845,75 +1552,6 @@ template <typename T> struct remove_reference<T&&> { using type = T; };
size_t size() const;
};
- template <typename T, class Allocator = allocator<T>>
- class deque {
- public:
- using value_type = T;
- using size_type = typename allocator_traits<Allocator>::size_type;
-
- // Constructors.
- deque() {}
- deque(size_type, const Allocator& = Allocator()) {}
- deque(initializer_list<T> initializer_list, const Allocator& = Allocator()) {}
- deque(const deque& deque) {}
- ~deque();
-
- // Modifiers.
- void push_back(const T& value);
- void push_back(T&& value);
- void push_front(const T& value);
- void push_front(T&& value);
- template <typename... Args>
- T& emplace_back(Args&&... args);
- template <typename... Args>
- T& emplace_front(Args&&... args);
-
- // Iterators
- class InputIterator {
- public:
- InputIterator(const InputIterator&);
- ~InputIterator();
- InputIterator& operator=(const InputIterator&);
- InputIterator& operator++();
- T& operator*() const;
- bool operator!=(const InputIterator&) const;
- bool operator==(const InputIterator&) const;
- };
- typedef InputIterator iterator;
- typedef const InputIterator const_iterator;
- iterator begin() noexcept;
- const_iterator begin() const noexcept;
- iterator end() noexcept;
- const_iterator end() const noexcept;
- T* data() noexcept;
- const T* data() const noexcept;
- T& operator[](size_t n);
- const T& operator[](size_t n) const;
- T& at(size_t n);
- const T& at(size_t n) const;
- size_t size() const;
- };
-
- template <class T, size_t N>
- struct array {
- using value_type = T;
- void fill(const T& value);
- void swap(array& other) noexcept;
- size_t size() const noexcept;
- size_t max_size() const noexcept;
- bool empty() const noexcept;
- T& operator[](size_t n) noexcept;
- const T& operator[](size_t n) const noexcept;
- T& at(size_t n);
- const T& at(size_t n) const;
- T& front() noexcept;
- const T& front() const noexcept;
- T& back() noexcept;
- const T& back() const noexcept;
- T* data() noexcept;
- const T* data() const noexcept;
- };
-
template <typename T>
struct default_delete {};
More information about the llvm-commits
mailing list