aboutsummaryrefslogtreecommitdiff
path: root/clang/unittests/Analysis
diff options
context:
space:
mode:
Diffstat (limited to 'clang/unittests/Analysis')
-rw-r--r--clang/unittests/Analysis/FlowSensitive/CMakeLists.txt2
-rw-r--r--clang/unittests/Analysis/FlowSensitive/MockHeaders.cpp12
-rw-r--r--clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTest.cpp34
-rw-r--r--clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp2679
-rw-r--r--clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.h159
5 files changed, 2880 insertions, 6 deletions
diff --git a/clang/unittests/Analysis/FlowSensitive/CMakeLists.txt b/clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
index 3508238..1d932ec 100644
--- a/clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
+++ b/clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
@@ -25,6 +25,8 @@ add_clang_unittest(ClangAnalysisFlowSensitiveTests
TransferTest.cpp
TypeErasedDataflowAnalysisTest.cpp
UncheckedOptionalAccessModelTest.cpp
+ UncheckedStatusOrAccessModelTest.cpp
+ UncheckedStatusOrAccessModelTestFixture.cpp
ValueTest.cpp
WatchedLiteralsSolverTest.cpp
CLANG_LIBS
diff --git a/clang/unittests/Analysis/FlowSensitive/MockHeaders.cpp b/clang/unittests/Analysis/FlowSensitive/MockHeaders.cpp
index d3dee58..d875542 100644
--- a/clang/unittests/Analysis/FlowSensitive/MockHeaders.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/MockHeaders.cpp
@@ -459,6 +459,10 @@ struct is_scalar
template <>
struct is_scalar<nullptr_t> : public true_type {};
+struct in_place_t {};
+
+constexpr in_place_t in_place;
+
} // namespace std
#endif // STD_TYPE_TRAITS_H
@@ -511,9 +515,8 @@ using remove_reference_t = typename std::remove_reference<T>::type;
template <typename T>
using decay_t = typename std::decay<T>::type;
-struct in_place_t {};
-
-constexpr in_place_t in_place;
+using std::in_place;
+using std::in_place_t;
} // namespace absl
#endif // ABSL_TYPE_TRAITS_H
@@ -589,9 +592,6 @@ static constexpr char StdOptionalHeader[] = R"(
namespace std {
-struct in_place_t {};
-constexpr in_place_t in_place;
-
struct nullopt_t {
constexpr explicit nullopt_t() {}
};
diff --git a/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTest.cpp b/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTest.cpp
new file mode 100644
index 0000000..20630a4c
--- /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 {
+
+auto Executor = std::make_unique<
+ UncheckedStatusOrAccessModelTestExecutor<UncheckedStatusOrAccessModel>>();
+
+INSTANTIATE_TEST_SUITE_P(
+ UncheckedStatusOrAccessModelTest, UncheckedStatusOrAccessModelTest,
+ testing::Values(
+ std::make_pair(Executor.get(),
+ UncheckedStatusOrAccessModelTestAliasKind::kUnaliased),
+ std::make_pair(
+ Executor.get(),
+ UncheckedStatusOrAccessModelTestAliasKind::kPartiallyAliased),
+ std::make_pair(
+ Executor.get(),
+ 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 0000000..cae9265
--- /dev/null
+++ b/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp
@@ -0,0 +1,2679 @@
+//===- UncheckedStatusOrAccessModelTestFixture.cpp ------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "UncheckedStatusOrAccessModelTestFixture.h"
+#include "MockHeaders.h"
+#include "llvm/Support/ErrorHandling.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "gtest/gtest.h"
+
+namespace clang::dataflow::statusor_model {
+namespace {
+
+TEST_P(UncheckedStatusOrAccessModelTest, NoStatusOrMention) {
+ ExpectDiagnosticsFor(R"cc(
+ void target() { "nop"; }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest,
+ UnwrapWithoutCheck_Lvalue_CallToValue) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, NonExplicitInitialization) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+ STATUSOR_INT target() {
+ STATUSOR_INT x = Make<STATUSOR_INT>();
+ return x.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest,
+ UnwrapWithoutCheck_Lvalue_CallToValue_NewLine) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ sor. // force newline
+ value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest,
+ UnwrapWithoutCheck_Rvalue_CallToValue) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ std::move(sor).value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest,
+ UnwrapWithoutCheck_Lvalue_CallToValueOrDie) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ sor.ValueOrDie(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest,
+ UnwrapWithoutCheck_Rvalue_CallToValueOrDie) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ std::move(sor).ValueOrDie(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest,
+ UnwrapWithoutCheck_Lvalue_CallToOperatorStar) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ *sor; // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest,
+ UnwrapWithoutCheck_Lvalue_CallToOperatorStarSeparateLine) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ * // [[unsafe]]
+ sor;
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest,
+ UnwrapWithoutCheck_Rvalue_CallToOperatorStar) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ *std::move(sor); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest,
+ UnwrapWithoutCheck_Lvalue_CallToOperatorArrow) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ struct Foo {
+ void foo();
+ };
+
+ void target(absl::StatusOr<Foo> sor) {
+ sor->foo(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest,
+ UnwrapWithoutCheck_Rvalue_CallToOperatorArrow) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ struct Foo {
+ void foo();
+ };
+
+ void target(absl::StatusOr<Foo> sor) {
+ std::move(sor)->foo(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, UnwrapRvalueWithCheck) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ if (sor.ok()) std::move(sor).value();
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, ParensInDeclInitExpr) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target() {
+ auto sor = (Make<STATUSOR_INT>());
+ if (sor.ok()) sor.value();
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, ReferenceInDeclInitExpr) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ struct Foo {
+ const STATUSOR_INT& GetStatusOrInt() const;
+ };
+
+ void target(Foo foo) {
+ auto sor = foo.GetStatusOrInt();
+ if (sor.ok()) sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ struct Foo {
+ STATUSOR_INT& GetStatusOrInt();
+ };
+
+ void target(Foo foo) {
+ auto sor = foo.GetStatusOrInt();
+ if (sor.ok()) sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ struct Foo {
+ STATUSOR_INT&& GetStatusOrInt() &&;
+ };
+
+ void target(Foo foo) {
+ auto sor = std::move(foo).GetStatusOrInt();
+ if (sor.ok()) sor.value();
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse) {
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ if (sor.ok())
+ sor.value();
+ else
+ sor.value(); // [[unsafe]]
+
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target() {
+ if (auto sor = Make<STATUSOR_INT>(); sor.ok())
+ sor.value();
+ else
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, JoinSafeSafe) {
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor, bool b) {
+ if (sor.ok()) {
+ if (b)
+ sor.value();
+ else
+ sor.value();
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, JoinUnsafeUnsafe) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor, bool b) {
+ if (b)
+ sor.value(); // [[unsafe]]
+ else
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, InversedIfThenElse) {
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ if (!sor.ok())
+ sor.value(); // [[unsafe]]
+ else
+ sor.value();
+
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, DoubleInversedIfThenElse) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ if (!!sor.ok())
+ sor.value();
+ else
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, TripleInversedIfThenElse) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ if (!!!sor.ok())
+ sor.value(); // [[unsafe]]
+ else
+ sor.value();
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_LhsAndRhs) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ if (x.ok() && y.ok()) {
+ x.value();
+
+ y.value();
+ } else {
+ x.value(); // [[unsafe]]
+
+ y.value(); // [[unsafe]]
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_NotLhsAndRhs) {
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ if (!x.ok() && y.ok()) {
+ y.value();
+
+ x.value(); // [[unsafe]]
+ } else {
+ x.value(); // [[unsafe]]
+
+ y.value(); // [[unsafe]]
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_LhsAndNotRhs) {
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ if (x.ok() && !y.ok()) {
+ x.value();
+
+ y.value(); // [[unsafe]]
+ } else {
+ x.value(); // [[unsafe]]
+
+ y.value(); // [[unsafe]]
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_NotLhsAndNotRhs) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ if (!x.ok() && !y.ok()) {
+ x.value(); // [[unsafe]]
+
+ y.value(); // [[unsafe]]
+ } else {
+ x.value(); // [[unsafe]]
+
+ y.value(); // [[unsafe]]
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_Not_LhsAndRhs) {
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ if (!(x.ok() && y.ok())) {
+ x.value(); // [[unsafe]]
+
+ y.value(); // [[unsafe]]
+ } else {
+ x.value();
+
+ y.value();
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_Not_NotLhsAndRhs) {
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ if (!(!x.ok() && y.ok())) {
+ x.value(); // [[unsafe]]
+
+ y.value(); // [[unsafe]]
+ } else {
+ y.value();
+
+ x.value(); // [[unsafe]]
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_Not_LhsAndNotRhs) {
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ if (!(x.ok() && !y.ok())) {
+ x.value(); // [[unsafe]]
+
+ y.value(); // [[unsafe]]
+ } else {
+ x.value();
+
+ y.value(); // [[unsafe]]
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_Not_NotLhsAndNotRhs) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ if (!(!x.ok() && !y.ok())) {
+ x.value(); // [[unsafe]]
+
+ y.value(); // [[unsafe]]
+ } else {
+ x.value(); // [[unsafe]]
+
+ y.value(); // [[unsafe]]
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_LhsOrRhs) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ if (x.ok() || y.ok()) {
+ x.value(); // [[unsafe]]
+
+ y.value(); // [[unsafe]]
+ } else {
+ x.value(); // [[unsafe]]
+
+ y.value(); // [[unsafe]]
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_NotLhsOrRhs) {
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ if (!x.ok() || y.ok()) {
+ x.value(); // [[unsafe]]
+
+ y.value(); // [[unsafe]]
+ } else {
+ x.value();
+
+ y.value(); // [[unsafe]]
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_LhsOrNotRhs) {
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ if (x.ok() || !y.ok()) {
+ x.value(); // [[unsafe]]
+
+ y.value(); // [[unsafe]]
+ } else {
+ y.value();
+
+ x.value(); // [[unsafe]]
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_NotLhsOrNotRhs) {
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ if (!x.ok() || !y.ok()) {
+ x.value(); // [[unsafe]]
+
+ y.value(); // [[unsafe]]
+ } else {
+ x.value();
+
+ y.value();
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_Not_LhsOrRhs) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ if (!(x.ok() || y.ok())) {
+ x.value(); // [[unsafe]]
+
+ y.value(); // [[unsafe]]
+ } else {
+ x.value(); // [[unsafe]]
+
+ y.value(); // [[unsafe]]
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_Not_NotLhsOrRhs) {
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ if (!(!x.ok() || y.ok())) {
+ x.value();
+
+ y.value(); // [[unsafe]]
+ } else {
+ x.value(); // [[unsafe]]
+
+ y.value(); // [[unsafe]]
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_Not_LhsOrNotRhs) {
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ if (!(x.ok() || !y.ok())) {
+ y.value();
+
+ x.value(); // [[unsafe]]
+ } else {
+ x.value(); // [[unsafe]]
+
+ y.value(); // [[unsafe]]
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_Not_NotLhsOrNotRhs) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ if (!(!x.ok() || !y.ok())) {
+ x.value();
+
+ y.value();
+ } else {
+ x.value(); // [[unsafe]]
+
+ y.value(); // [[unsafe]]
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, TerminatingIfThenBranch) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ if (!sor.ok()) return;
+
+ sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ if (sor.ok()) return;
+
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ if (!x.ok() || !y.ok()) return;
+
+ x.value();
+
+ y.value();
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, TerminatingIfElseBranch) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ if (sor.ok()) {
+ } else {
+ return;
+ }
+
+ sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ if (!sor.ok()) {
+ } else {
+ return;
+ }
+
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, TerminatingIfThenBranchInLoop) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ while (Make<bool>()) {
+ if (!sor.ok()) continue;
+
+ sor.value();
+ }
+
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ while (Make<bool>()) {
+ if (!sor.ok()) break;
+
+ sor.value();
+ }
+
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, TernaryConditionalOperator) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ sor.ok() ? sor.value() : 21;
+
+ sor.ok() ? 21 : sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ !sor.ok() ? 21 : sor.value();
+
+ !sor.ok() ? sor.value() : 21; // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor1, STATUSOR_INT sor2) {
+ !((__builtin_expect(false || (!(sor1.ok() && sor2.ok())), false)))
+ ? (void)0
+ : (void)1;
+ do {
+ sor1.value(); // [[unsafe]]
+ sor2.value(); // [[unsafe]]
+ } while (true);
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ while (Make<bool>()) sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ while (sor.ok()) sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ while (!sor.ok()) sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ while (!!sor.ok()) sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ while (!!!sor.ok()) sor.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_LhsAndRhs) {
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (x.ok() && y.ok()) {
+ x.value();
+
+ y.value();
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_NotLhsAndRhs) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (!x.ok() && y.ok()) x.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (!x.ok() && y.ok()) y.value();
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_LhsAndNotRhs) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (x.ok() && !y.ok()) x.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (x.ok() && !y.ok()) y.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_NotLhsAndNotRhs) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (!x.ok() && !y.ok()) x.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (!x.ok() && !y.ok()) y.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_Not_LhsAndRhs) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (!(x.ok() && y.ok())) x.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (!(x.ok() && y.ok())) y.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_Not_NotLhsAndRhs) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (!(!x.ok() && y.ok())) x.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (!(!x.ok() && y.ok())) y.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_Not_LhsAndNotRhs) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (!(x.ok() && !y.ok())) x.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (!(x.ok() && !y.ok())) y.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_Not_NotLhsAndNotRhs) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (!(!x.ok() && !y.ok())) x.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (!(!x.ok() && !y.ok())) y.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_LhsOrRhs) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (x.ok() || y.ok()) x.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (x.ok() || y.ok()) y.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_NotLhsOrRhs) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (!x.ok() || y.ok()) x.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (!x.ok() || y.ok()) y.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_LhsOrNotRhs) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (x.ok() || !y.ok()) x.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (x.ok() || !y.ok()) y.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_NotLhsOrNotRhs) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (!x.ok() || !y.ok()) x.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (!x.ok() || !y.ok()) y.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_Not_LhsOrRhs) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (!(x.ok() || y.ok())) x.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (!(x.ok() || y.ok())) y.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_Not_NotLhsOrRhs) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (!(!x.ok() || y.ok())) x.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (!(!x.ok() || y.ok())) y.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_Not_LhsOrNotRhs) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (!(x.ok() || !y.ok())) x.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (!(x.ok() || !y.ok())) y.value();
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_Not_NotLhsOrNotRhs) {
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (!(!x.ok() || !y.ok())) {
+ x.value();
+
+ y.value();
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_AccessAfterStmt) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ while (sor.ok()) {
+ }
+
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ while (!sor.ok()) {
+ }
+
+ sor.value();
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_TerminatingBranch_Return) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ while (!sor.ok()) return;
+
+ sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ while (sor.ok()) return;
+
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_NestedIfWithBinaryCondition) {
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (Make<bool>()) {
+ if (x.ok() && y.ok()) {
+ x.value();
+
+ y.value();
+ }
+ }
+ }
+ )cc");
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ while (Make<bool>()) {
+ if (!(!x.ok() || !y.ok())) {
+ x.value();
+
+ y.value();
+ }
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, BuiltinExpect) {
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT x, STATUSOR_INT y) {
+ if (!__builtin_expect(!x.ok() || __builtin_expect(!y.ok(), true), false)) {
+ x.value();
+
+ y.value();
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, CopyAssignment) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target() {
+ STATUSOR_INT sor = Make<STATUSOR_INT>();
+ if (sor.ok()) {
+ sor = Make<STATUSOR_INT>();
+ sor.value(); // [[unsafe]]
+ }
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target() {
+ STATUSOR_INT sor = Make<STATUSOR_INT>();
+ if (!sor.ok()) return;
+
+ sor = Make<STATUSOR_INT>();
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target() {
+ STATUSOR_INT x = Make<STATUSOR_INT>();
+ if (x.ok()) {
+ STATUSOR_INT y = x;
+ x = Make<STATUSOR_INT>();
+
+ y.value();
+
+ x.value(); // [[unsafe]]
+ }
+ }
+ )cc");
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target() {
+ STATUSOR_INT x = Make<STATUSOR_INT>();
+ STATUSOR_INT y = x;
+ if (!y.ok()) return;
+
+ x.value();
+
+ y = Make<STATUSOR_INT>();
+ x.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ struct Foo {
+ STATUSOR_INT bar;
+ };
+
+ void target(Foo foo) {
+ foo.bar = Make<STATUSOR_INT>();
+ if (foo.bar.ok()) {
+ foo.bar.value();
+
+ foo.bar = Make<STATUSOR_INT>();
+ foo.bar.value(); // [[unsafe]]
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, ShortCircuitingBinaryOperators) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_BOOL sor) {
+ bool b = sor.ok() & sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_BOOL sor) {
+ bool b = sor.ok() && sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_BOOL sor) {
+ bool b = !sor.ok() && sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_BOOL sor) {
+ bool b = sor.ok() || sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_BOOL sor) {
+ bool b = !sor.ok() || sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(bool b, STATUSOR_INT sor) {
+ if (b || sor.ok()) {
+ do {
+ sor.value(); // [[unsafe]]
+ } while (true);
+ }
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(bool b, STATUSOR_INT sor) {
+ if (__builtin_expect(b || sor.ok(), false)) {
+ do {
+ sor.value(); // [[unsafe]]
+ } while (false);
+ }
+ }
+ )cc");
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor1, STATUSOR_INT sor2) {
+ while (sor1.ok() && sor2.ok()) sor1.value();
+ while (sor1.ok() && sor2.ok()) sor2.value();
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, References) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target() {
+ STATUSOR_INT x = Make<STATUSOR_INT>();
+ STATUSOR_INT& y = x;
+ if (x.ok()) {
+ x.value();
+
+ y.value();
+ } else {
+ x.value(); // [[unsafe]]
+
+ y.value(); // [[unsafe]]
+ }
+ }
+ )cc");
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target() {
+ STATUSOR_INT x = Make<STATUSOR_INT>();
+ STATUSOR_INT& y = x;
+ if (y.ok()) {
+ x.value();
+
+ y.value();
+ } else {
+ x.value(); // [[unsafe]]
+
+ y.value(); // [[unsafe]]
+ }
+ }
+ )cc");
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target() {
+ STATUSOR_INT x = Make<STATUSOR_INT>();
+ STATUSOR_INT& y = x;
+ if (!y.ok()) return;
+
+ x.value();
+
+ y = Make<STATUSOR_INT>();
+ x.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target() {
+ STATUSOR_INT x = Make<STATUSOR_INT>();
+ const STATUSOR_INT& y = x;
+ if (!y.ok()) return;
+
+ y.value();
+
+ x = Make<STATUSOR_INT>();
+ y.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, NoReturnAttribute) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ __attribute__((noreturn)) void f();
+
+ void target(STATUSOR_INT sor) {
+ if (!sor.ok()) f();
+
+ sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void f();
+
+ void target(STATUSOR_INT sor) {
+ if (!sor.ok()) f();
+
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ struct Foo {
+ __attribute__((noreturn)) ~Foo();
+ void Bar();
+ };
+
+ void target(STATUSOR_INT sor) {
+ if (!sor.ok()) Foo().Bar();
+
+ sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ struct Foo {
+ ~Foo();
+ void Bar();
+ };
+
+ void target(STATUSOR_INT sor) {
+ if (!sor.ok()) Foo().Bar();
+
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void f();
+ __attribute__((noreturn)) void g();
+
+ void target(STATUSOR_INT sor) {
+ sor.ok() ? f() : g();
+
+ sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ __attribute__((noreturn)) void f();
+ void g();
+
+ void target(STATUSOR_INT sor) {
+ !sor.ok() ? f() : g();
+
+ sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void f();
+ void g();
+
+ void target(STATUSOR_INT sor) {
+ sor.ok() ? f() : g();
+
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void terminate() __attribute__((noreturn));
+
+ void target(STATUSOR_INT sor) {
+ sor.value(); // [[unsafe]]
+ terminate();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void terminate() __attribute__((noreturn));
+
+ void target(STATUSOR_INT sor) {
+ if (sor.ok()) sor.value();
+ terminate();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void terminate() __attribute__((noreturn));
+
+ struct Foo {
+ ~Foo() __attribute__((noreturn));
+ };
+
+ void target() {
+ auto sor = Make<absl::StatusOr<Foo>>();
+ !(false || !(sor.ok())) ? (void)0 : terminate();
+ sor.value();
+ terminate();
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, DeclInLoop) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ while (auto ok = sor.ok()) sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ using BoolAlias = bool;
+
+ void target(STATUSOR_INT sor) {
+ while (BoolAlias ok = sor.ok()) sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target() {
+ while (Make<bool>()) {
+ STATUSOR_INT sor = Make<STATUSOR_INT>();
+ sor.value(); // [[unsafe]]
+ }
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ using StatusOrInt = STATUSOR_INT;
+
+ void target() {
+ while (Make<bool>()) {
+ StatusOrInt sor = Make<STATUSOR_INT>();
+ sor.value(); // [[unsafe]]
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, NonEvaluatedExprInCondition) {
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ bool unknown();
+
+ void target(STATUSOR_INT sor) {
+ if (unknown() && sor.ok()) sor.value();
+ if (sor.ok() && unknown()) sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ bool unknown();
+
+ void target(STATUSOR_INT sor) {
+ if (!(!unknown() || !sor.ok())) sor.value();
+ if (!(!sor.ok() || !unknown())) sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ bool unknown();
+
+ void target(STATUSOR_INT sor) {
+ if (unknown() || sor.ok()) sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ bool unknown();
+
+ void target(STATUSOR_INT sor) {
+ if (sor.ok() || unknown()) sor.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, CorrelatedBranches) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(bool b, STATUSOR_INT sor) {
+ if (b || sor.ok()) {
+ if (!b) sor.value();
+ }
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(bool b, STATUSOR_INT sor) {
+ if (b && !sor.ok()) return;
+ if (b) sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(bool b, STATUSOR_INT sor) {
+ if (sor.ok()) b = true;
+ if (b) sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(bool b, STATUSOR_INT sor) {
+ if (b) return;
+ if (sor.ok()) b = true;
+ if (b) sor.value();
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, ConditionWithInitStmt) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target() {
+ if (STATUSOR_INT sor = Make<STATUSOR_INT>(); sor.ok()) sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target() {
+ if (STATUSOR_INT sor = Make<STATUSOR_INT>(); !sor.ok())
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, DeadCode) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ bool b = false;
+ if (b) sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ bool b;
+ b = false;
+ if (b) sor.value();
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, TemporaryDestructors) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ sor.ok() ? sor.value() : Fatal().value();
+
+ sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ !sor.ok() ? Fatal().value() : sor.value();
+
+ sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(bool b, STATUSOR_INT sor) {
+ b ? 0 : sor.ok() ? sor.value() : Fatal().value();
+
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(bool b, STATUSOR_INT sor) {
+ for (int i = 0; i < 10; i++) {
+ (b && sor.ok()) ? sor.value() : Fatal().value();
+
+ if (b) sor.value();
+ }
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(bool b, STATUSOR_INT sor) {
+ for (int i = 0; i < 10; i++) {
+ (b || !sor.ok()) ? Fatal().value() : 0;
+
+ if (!b) sor.value();
+ }
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(bool b, STATUSOR_INT sor) {
+ for (int i = 0; i < 10; i++) {
+ (false || !(b && sor.ok())) ? Fatal().value() : 0;
+
+ do {
+ sor.value();
+ } while (b);
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, CheckMacro) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ CHECK(sor.ok());
+ sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ CHECK(!sor.ok());
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, QcheckMacro) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ QCHECK(sor.ok());
+ sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ QCHECK(!sor.ok());
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, CheckNeMacro) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ CHECK_NE(sor.status(), absl::OkStatus());
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, QcheckNeMacro) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ QCHECK_NE(sor.status(), absl::OkStatus());
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, GlobalVars) {
+ // The following examples are not sound as there could be opaque calls between
+ // the ok() and the value() calls that change the StatusOr value.
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ static STATUSOR_INT sor;
+
+ void target() {
+ if (sor.ok())
+ sor.value();
+ else
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target() {
+ static STATUSOR_INT sor;
+ if (sor.ok())
+ sor.value();
+ else
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ struct Foo {
+ static STATUSOR_INT sor;
+ };
+
+ void target(Foo foo) {
+ if (foo.sor.ok())
+ foo.sor.value();
+ else
+ foo.sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ struct Foo {
+ static STATUSOR_INT sor;
+ };
+
+ void target() {
+ if (Foo::sor.ok())
+ Foo::sor.value();
+ else
+ Foo::sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ struct Foo {
+ static STATUSOR_INT sor;
+
+ static void target() {
+ if (sor.ok())
+ sor.value();
+ else
+ sor.value(); // [[unsafe]]
+ }
+ };
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ struct Foo {
+ static STATUSOR_INT sor;
+
+ void target() {
+ if (sor.ok())
+ sor.value();
+ else
+ sor.value(); // [[unsafe]]
+ }
+ };
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ struct S {
+ static const int x = -1;
+ };
+
+ int target(S s) { return s.x; }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, ReferenceReceivers) {
+ // The following examples are not sound as there could be opaque calls between
+ // the ok() and the value() calls that change the StatusOr value. However,
+ // this is the behavior that users expect so it is here to stay.
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT& sor) {
+ if (sor.ok())
+ sor.value();
+ else
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ struct Foo {
+ STATUSOR_INT& sor;
+ };
+
+ void target(Foo foo) {
+ if (foo.sor.ok())
+ foo.sor.value();
+ else
+ foo.sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ struct Bar {
+ STATUSOR_INT sor;
+ };
+
+ struct Foo {
+ Bar& bar;
+ };
+
+ void target(Foo foo) {
+ if (foo.bar.sor.ok())
+ foo.bar.sor.value();
+ else
+ foo.bar.sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ struct Foo {
+ STATUSOR_INT& sor;
+ };
+
+ void target(Foo& foo) {
+ if (foo.sor.ok())
+ foo.sor.value();
+ else
+ foo.sor.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, Lambdas) {
+ ExpectDiagnosticsForLambda(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target() {
+ [](STATUSOR_INT sor) {
+ if (sor.ok())
+ sor.value();
+ else
+ sor.value(); // [[unsafe]]
+ }(Make<STATUSOR_INT>());
+ }
+ )cc");
+ ExpectDiagnosticsForLambda(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ [sor]() {
+ if (sor.ok())
+ sor.value();
+ else
+ sor.value(); // [[unsafe]]
+ }();
+ }
+ )cc");
+ ExpectDiagnosticsForLambda(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ [&sor]() {
+ if (sor.ok())
+ sor.value();
+ else
+ sor.value(); // [[unsafe]]
+ }();
+ }
+ )cc");
+ ExpectDiagnosticsForLambda(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ [sor2 = sor]() {
+ if (sor2.ok())
+ sor2.value();
+ else
+ sor2.value(); // [[unsafe]]
+ }();
+ }
+ )cc");
+ ExpectDiagnosticsForLambda(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ [&]() {
+ if (sor.ok())
+ sor.value();
+ else
+ sor.value(); // [[unsafe]]
+ }();
+ }
+ )cc");
+ ExpectDiagnosticsForLambda(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ [=]() {
+ if (sor.ok())
+ sor.value();
+ else
+ sor.value(); // [[unsafe]]
+ }();
+ }
+ )cc");
+ ExpectDiagnosticsForLambda(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ struct Foo {
+ STATUSOR_INT sor;
+
+ void target() {
+ [this]() {
+ if (sor.ok())
+ sor.value();
+ else
+ sor.value(); // [[unsafe]]
+ }();
+ }
+ };
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, GoodLambda) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ int target() {
+ STATUSOR_INT sor = Make<STATUSOR_INT>();
+ if (sor.ok()) return [&s = sor.value()] { return s; }();
+ return 0;
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, Status) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void foo();
+
+ void target(STATUS s) {
+ if (s.ok()) foo();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void foo();
+
+ void target() {
+ STATUS s = Make<STATUSOR_INT>().status();
+ if (s.ok()) foo();
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, ExpectThatMacro) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ EXPECT_THAT(sor, testing::status::IsOk());
+
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ EXPECT_THAT(sor.status(), testing::status::IsOk());
+
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target() {
+ STATUSOR_INT sor = Make<STATUSOR_INT>();
+ EXPECT_THAT(sor, testing::status::IsOk());
+
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, ExpectOkMacro) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ EXPECT_OK(sor);
+
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ EXPECT_OK(sor.status());
+
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target() {
+ STATUSOR_INT sor = Make<STATUSOR_INT>();
+ EXPECT_OK(sor);
+
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, BreadthFirstBlockTraversalLoop) {
+ // Evaluating the CFG blocks of the code below in breadth-first order results
+ // in an infinite loop. Each iteration of the while loop below results in a
+ // new value being assigned to the storage location of sor1. However,
+ // following a bread-first order of evaluation, downstream blocks will join
+ // environments of different generations of predecessor blocks having distinct
+ // values assigned to the sotrage location of sor1, resulting in not assigning
+ // a value to the storage location of sor1 in successors. As iterations of the
+ // analysis go, the state of the environment flips between having a value
+ // assigned to the storage location of sor1 and not having a value assigned to
+ // it. Since the evaluation of the copy constructor expression in bar(sor1)
+ // depends on a value being assigned to sor1, the state of the environment
+ // also flips between having a storage location assigned to the bar(sor1)
+ // expression and not having a storage location assigned to it. This leads to
+ // an infinite loop as the environment can't stabilize.
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void foo(int, int);
+ STATUSOR_INT bar(STATUSOR_INT);
+ void baz(int);
+
+ void target() {
+ while (true) {
+ STATUSOR_INT sor1 = Make<STATUSOR_INT>();
+ if (sor1.ok()) {
+ STATUSOR_INT sor2 = Make<STATUSOR_INT>();
+ if (sor2.ok()) foo(sor1.value(), sor2.value());
+ }
+
+ STATUSOR_INT sor3 = bar(sor1);
+ for (int i = 0; i < 5; i++) sor3 = bar(sor1);
+
+ baz(sor3.value()); // [[unsafe]]
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, ReturnValue) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target() {
+ STATUSOR_INT sor = Make<STATUSOR_INT>();
+ if (sor.ok())
+ sor.value();
+ else
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, Goto) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ label:
+ if (sor.ok())
+ sor.value();
+ else
+ sor.value(); // [[unsafe]]
+ goto label;
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ label:
+ if (!sor.ok()) goto label;
+ sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ if (!sor.ok()) return;
+ goto label;
+ label:
+ sor.value();
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, JoinDistinctValues) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(bool b) {
+ STATUSOR_INT sor;
+ if (b)
+ sor = Make<STATUSOR_INT>();
+ else
+ sor = Make<STATUSOR_INT>();
+
+ if (sor.ok())
+ sor.value();
+ else
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(bool b) {
+ STATUSOR_INT sor;
+ if (b) {
+ sor = Make<STATUSOR_INT>();
+ if (!sor.ok()) return;
+ } else {
+ sor = Make<STATUSOR_INT>();
+ if (!sor.ok()) return;
+ }
+ sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(bool b) {
+ STATUSOR_INT sor;
+ if (b) {
+ sor = Make<STATUSOR_INT>();
+ if (!sor.ok()) return;
+ } else {
+ sor = Make<STATUSOR_INT>();
+ }
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, VarDeclInitExprFromPairAccess) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target() {
+ auto sor = Make<std::pair<int, STATUSOR_INT>>().second;
+ if (sor.ok())
+ sor.value();
+ else
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target() {
+ const auto& sor = Make<std::pair<int, STATUSOR_INT>>().second;
+ if (sor.ok())
+ sor.value();
+ else
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, LValueToRValueCastOfChangingValue) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ bool foo();
+
+ void target(bool b1) {
+ STATUSOR_INT sor;
+ if (b1)
+ sor = Make<STATUSOR_INT>();
+ else
+ sor = Make<STATUSOR_INT>();
+
+ do {
+ const auto& b2 = foo();
+ if (b2) break;
+
+ sor.value(); // [[unsafe]]
+ } while (true);
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, ConstructorInitializer) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ class target {
+ target() : foo_(Make<STATUSOR_INT>().value()) { // [[unsafe]]
+ }
+ int foo_;
+ };
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, AssignStatusToBoolVar) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ bool ok = sor.ok();
+ if (ok)
+ sor.value();
+ else
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ bool not_ok = !sor.ok();
+ if (not_ok)
+ sor.value(); // [[unsafe]]
+ else
+ sor.value();
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, StructuredBindings) {
+ // Binding to a pair (which is actually a struct in the mock header).
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target() {
+ const auto [sor, x] = Make<std::pair<STATUSOR_INT, int>>();
+ if (sor.ok()) sor.value();
+ }
+ )cc");
+
+ // Unsafe case.
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target() {
+ const auto [sor, x] = Make<std::pair<STATUSOR_INT, int>>();
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+
+ // As a reference.
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target() {
+ const auto& [sor, x] = Make<std::pair<STATUSOR_INT, int>>();
+ if (sor.ok()) sor.value();
+ }
+ )cc");
+
+ // Binding to a ref in a struct.
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ struct S {
+ STATUSOR_INT& sor;
+ int i;
+ };
+
+ void target() {
+ const auto& [sor, i] = Make<S>();
+ if (sor.ok()) sor.value();
+ }
+ )cc");
+
+ // In a loop.
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target() {
+ auto vals = Make<std::vector<std::pair<int, STATUSOR_INT>>>();
+ for (const auto& [x, sor] : vals)
+ if (sor.ok()) sor.value();
+ }
+ )cc");
+
+ // Similar to the above, but InitExpr already has the storage initialized,
+ // and bindings refer to them.
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target() {
+ auto vals = Make<std::vector<std::pair<int, STATUSOR_INT>>>();
+ for (const auto& p : vals) {
+ const auto& [i, sor] = p;
+ if (sor.ok()) sor.value();
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, AssignCompositeLogicExprToVar) {
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor, bool b) {
+ bool c = sor.ok() && b;
+ if (c) sor.value();
+ }
+ )cc");
+
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor, bool b) {
+ bool c = !(!sor.ok() || !b);
+ if (c) sor.value();
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, Subclass) {
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ class Foo : public STATUSOR_INT {};
+
+ void target(Foo opt) {
+ opt.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, SubclassStatus) {
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ class Foo : public STATUS {};
+
+ void target(Foo opt) { opt.ok(); }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, SubclassOk) {
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ class Foo : public STATUSOR_INT {};
+
+ void target(Foo opt) {
+ if (opt.ok()) opt.value();
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, SubclassOperator) {
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ class Foo : public STATUSOR_INT {};
+
+ void target(Foo opt) {
+ *opt; // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, UnwrapValueWithStatusCheck) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ if (sor.status().ok())
+ sor.value();
+ else
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, UnwrapValueWithStatusRefCheck) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ const STATUS& s = sor.status();
+ if (s.ok())
+ sor.value();
+ else
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, UnwrapValueWithStatusPtrCheck) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ const STATUS* s = &sor.status();
+ if (s->ok())
+ sor.value();
+ else
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, UnwrapValueWithMovedStatus) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ if (std::move(sor.status()).ok())
+ sor.value();
+ else
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, MembersUsedInsideStatus) {
+ ExpectDiagnosticsFor(R"cc(
+ namespace absl {
+
+ class Status {
+ public:
+ bool ok() const;
+
+ void target() const { ok(); }
+ };
+
+ } // namespace absl
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, StatusUpdate) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ STATUS s;
+ s.Update(sor.status());
+ if (s.ok())
+ sor.value();
+ else
+ sor.value(); // [[unsafe]]
+ }
+ )cc");
+
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor1, STATUSOR_INT sor2) {
+ STATUS s;
+ s.Update(sor1.status());
+ s.Update(sor2.status());
+ if (s.ok()) {
+ sor1.value();
+ sor2.value();
+ }
+ }
+ )cc");
+
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor1, STATUSOR_INT sor2) {
+ STATUS s;
+ s.Update(sor1.status());
+ CHECK(s.ok());
+ s.Update(sor2.status());
+ sor1.value();
+ sor2.value(); // [[unsafe]]
+ }
+ )cc");
+
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor1, STATUSOR_INT sor2) {
+ STATUS s;
+ s.Update(sor1.status());
+ CHECK(s.ok());
+ sor1.value();
+ sor2.value(); // [[unsafe]]
+ }
+ )cc");
+
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor1, STATUSOR_INT sor2) {
+ STATUS s;
+ STATUS sor1_status = sor1.status();
+ s.Update(std::move(sor1_status));
+ CHECK(s.ok());
+ sor1.value();
+ sor2.value(); // [[unsafe]]
+ }
+ )cc");
+
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor1, STATUSOR_INT sor2) {
+ STATUS s;
+ STATUS sor1_status = sor1.status();
+ sor1_status.Update(sor2.status());
+ s.Update(std::move(sor1_status));
+ CHECK(s.ok());
+ sor1.value();
+ sor2.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ const STATUS& OptStatus();
+
+ void target(STATUSOR_INT sor) {
+ auto s = sor.status();
+ s.Update(OptStatus());
+ if (s.ok()) sor.value();
+ }
+ )cc");
+}
+
+} // namespace
+
+std::string
+GetAliasMacros(UncheckedStatusOrAccessModelTestAliasKind AliasKind) {
+ switch (AliasKind) {
+ case UncheckedStatusOrAccessModelTestAliasKind::kUnaliased:
+ return R"cc(
+#define STATUSOR_INT ::absl::StatusOr<int>
+#define STATUSOR_BOOL ::absl::StatusOr<bool>
+#define STATUSOR_VOIDPTR ::absl::StatusOr<void*>
+#define STATUS ::absl::Status
+ )cc";
+ case UncheckedStatusOrAccessModelTestAliasKind::kPartiallyAliased:
+ return R"cc(
+ template <typename T>
+ using StatusOrAlias = ::absl::StatusOr<T>;
+#define STATUSOR_INT StatusOrAlias<int>
+#define STATUSOR_BOOL StatusOrAlias<bool>
+#define STATUSOR_VOIDPTR StatusOrAlias<void*>
+#define STATUS ::absl::Status
+ )cc";
+ case UncheckedStatusOrAccessModelTestAliasKind::kFullyAliased:
+ return R"cc(
+ using StatusOrIntAlias = ::absl::StatusOr<int>;
+#define STATUSOR_INT StatusOrIntAlias
+ using StatusOrBoolAlias = ::absl::StatusOr<bool>;
+#define STATUSOR_BOOL StatusOrBoolAlias
+ using StatusOrVoidPtrAlias = ::absl::StatusOr<void*>;
+#define STATUSOR_VOIDPTR StatusOrVoidPtrAlias
+ using StatusAlias = ::absl::Status;
+#define STATUS StatusAlias
+ )cc";
+ }
+ llvm_unreachable("Unknown alias kind.");
+}
+
+std::vector<std::pair<std::string, std::string>>
+GetHeaders(UncheckedStatusOrAccessModelTestAliasKind AliasKind) {
+ auto Headers = test::getMockHeaders();
+
+ Headers.emplace_back("unchecked_statusor_access_test_defs.h",
+ R"cc(
+#include "cstddef.h"
+#include "statusor_defs.h"
+#include "std_optional.h"
+#include "std_vector.h"
+#include "std_pair.h"
+#include "absl_log.h"
+#include "testing_defs.h"
+
+ template <typename T>
+ T Make();
+
+ class Fatal {
+ public:
+ ~Fatal() __attribute__((noreturn));
+ int value();
+ };
+ )cc" +
+ GetAliasMacros(AliasKind));
+ return Headers;
+}
+} // namespace clang::dataflow::statusor_model
diff --git a/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.h b/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.h
new file mode 100644
index 0000000..92dcd93
--- /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
+ 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));
+ }
+
+ ~UncheckedStatusOrAccessModelTestExecutor() override = default;
+};
+
+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_