blob: 5635ff4e01d36e617fc3e1971c68abcb7a787f74 [file] [log] [blame] [edit]
//===- 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");
}
TEST_P(UncheckedStatusOrAccessModelTest, EqualityCheck) {
ExpectDiagnosticsFor(
R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target(STATUSOR_INT x, STATUSOR_INT y) {
if (x.ok()) {
if (x == y)
y.value();
else
y.value(); // [[unsafe]]
}
}
)cc");
ExpectDiagnosticsFor(
R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target(STATUSOR_INT x, STATUSOR_INT y) {
if (x.ok()) {
if (y == x)
y.value();
else
y.value(); // [[unsafe]]
}
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target(STATUSOR_INT x, STATUSOR_INT y) {
if (x.ok()) {
if (x != y)
y.value(); // [[unsafe]]
else
y.value();
}
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target(STATUSOR_INT x, STATUSOR_INT y) {
if (x.ok()) {
if (y != x)
y.value(); // [[unsafe]]
else
y.value();
}
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target(STATUSOR_INT x, STATUSOR_INT y) {
if (x.ok()) {
if (!(x == y))
y.value(); // [[unsafe]]
else
y.value();
}
}
)cc");
ExpectDiagnosticsFor(
R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target(STATUSOR_INT x, STATUSOR_INT y) {
if (x.ok()) {
if (!(x != y))
y.value();
else
y.value(); // [[unsafe]]
}
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target(STATUSOR_INT x, STATUSOR_INT y) {
if (x == y)
if (x.ok()) y.value();
}
)cc");
ExpectDiagnosticsFor(
R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target(STATUSOR_INT x, STATUSOR_INT y) {
if (x.ok()) {
if (x.status() == y.status())
y.value();
else
y.value(); // [[unsafe]]
}
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target(STATUSOR_INT x, STATUSOR_INT y) {
if (x.ok()) {
if (x.status() != y.status())
y.value(); // [[unsafe]]
else
y.value();
}
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target(STATUSOR_INT sor) {
if (sor.status() == absl::OkStatus())
sor.value();
else
sor.value(); // [[unsafe]]
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target(STATUSOR_INT sor) {
if (sor.status() != absl::OkStatus())
sor.value(); // [[unsafe]]
else
sor.value();
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target(STATUSOR_INT sor) {
if (sor.status() != absl::InvalidArgumentError("oh no"))
sor.value(); // [[unsafe]]
else
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()) {
if (x.ok() == y.ok())
y.value();
else
y.value(); // [[unsafe]]
}
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target(STATUSOR_INT x, STATUSOR_INT y) {
if (x.ok()) {
if (x.ok() != y.ok())
y.value(); // [[unsafe]]
else
y.value();
}
}
)cc");
ExpectDiagnosticsFor(
R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target(STATUSOR_INT x, STATUSOR_INT y) {
if (x.ok()) {
if (x.status().ok() == y.status().ok())
y.value();
else
y.value(); // [[unsafe]]
}
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target(STATUSOR_INT x, STATUSOR_INT y) {
if (x.ok()) {
if (x.status().ok() != y.status().ok())
y.value(); // [[unsafe]]
else
y.value();
}
}
)cc");
ExpectDiagnosticsFor(
R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target(STATUSOR_INT x, STATUSOR_INT y) {
if (x.ok()) {
if (x.status().ok() == y.ok())
y.value();
else
y.value(); // [[unsafe]]
}
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target(STATUSOR_INT x, STATUSOR_INT y) {
if (x.ok()) {
if (x.status().ok() != y.ok())
y.value(); // [[unsafe]]
else
y.value();
}
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target(bool b, STATUSOR_INT sor) {
if (sor.ok() == b) {
if (b) sor.value();
}
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target(STATUSOR_INT sor) {
if (sor.ok() == true) sor.value();
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target(STATUSOR_INT sor) {
if (sor.ok() == false) sor.value(); // [[unsafe]]
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target(bool b) {
STATUSOR_INT sor1;
STATUSOR_INT sor2 = Make<STATUSOR_INT>();
if (sor1 == sor2) sor2.value(); // [[unsafe]]
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target(bool b) {
STATUSOR_INT sor1 = Make<STATUSOR_INT>();
STATUSOR_INT sor2;
if (sor1 == sor2) sor1.value(); // [[unsafe]]
}
)cc");
}
TEST_P(UncheckedStatusOrAccessModelTest, PointerEqualityCheck) {
ExpectDiagnosticsFor(
R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target(STATUSOR_INT* x, STATUSOR_INT* y) {
if (x->ok()) {
if (x == y)
y->value();
else
y->value(); // [[unsafe]]
}
}
)cc");
ExpectDiagnosticsFor(
R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target(STATUSOR_INT* x, STATUSOR_INT* y) {
if (x->ok()) {
if (x != y)
y->value(); // [[unsafe]]
else
y->value();
}
}
)cc");
ExpectDiagnosticsFor(
R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target(STATUS* x, STATUS* y) {
auto sor = Make<STATUSOR_INT>();
if (x->ok()) {
if (x == y && sor.status() == *y)
sor.value();
else
sor.value(); // [[unsafe]]
}
}
)cc");
ExpectDiagnosticsFor(
R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target(STATUS* x, STATUS* y) {
auto sor = Make<STATUSOR_INT>();
if (x->ok()) {
if (x != y)
sor.value(); // [[unsafe]]
else if (sor.status() == *y)
sor.value();
}
}
)cc");
}
TEST_P(UncheckedStatusOrAccessModelTest, Emplace) {
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
struct Foo {
Foo(int);
};
void target(absl::StatusOr<Foo> sor, int value) {
sor.emplace(value);
sor.value();
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
struct Foo {
Foo(std::initializer_list<int>, int);
};
void target(absl::StatusOr<Foo> sor, int value) {
sor.emplace({1, 2, 3}, value);
sor.value();
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target() {
STATUSOR_INT sor;
bool sor_ok = sor.ok();
if (!sor_ok)
sor.emplace(42);
sor.value();
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target(bool b) {
STATUSOR_INT sor;
if (b) sor.emplace(42);
if (b) sor.value();
}
)cc");
}
TEST_P(UncheckedStatusOrAccessModelTest, ValueConstruction) {
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target() {
STATUSOR_BOOL result = false;
result.value();
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target() {
STATUSOR_INT result = 21;
result.value();
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target() {
STATUSOR_INT result = Make<STATUSOR_INT>();
result.value(); // [[unsafe]]
}
)cc");
ExpectDiagnosticsFor(
R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target() {
STATUSOR_BOOL result = false;
if (result.ok())
result.value();
else
result.value();
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target() {
STATUSOR_BOOL result(false);
result.value();
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target() {
STATUSOR_INT result(21);
result.value();
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target() {
STATUSOR_INT result(Make<STATUSOR_INT>());
result.value(); // [[unsafe]]
}
)cc");
ExpectDiagnosticsFor(
R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target() {
STATUSOR_BOOL result(false);
if (result.ok())
result.value();
else
result.value();
}
)cc");
}
TEST_P(UncheckedStatusOrAccessModelTest, ValueAssignment) {
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target() {
STATUSOR_BOOL result;
result = false;
result.value();
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target() {
STATUSOR_INT result;
result = 21;
result.value();
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target() {
STATUSOR_INT result;
result = Make<STATUSOR_INT>();
result.value(); // [[unsafe]]
}
)cc");
ExpectDiagnosticsFor(
R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target() {
STATUSOR_BOOL result;
result = false;
if (result.ok())
result.value();
else
result.value();
}
)cc");
}
TEST_P(UncheckedStatusOrAccessModelTest, NestedStatusOr) {
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target() {
absl::StatusOr<STATUSOR_INT> result;
result = Make<STATUSOR_INT>();
result.value();
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target() {
absl::StatusOr<STATUSOR_INT> result = Make<STATUSOR_INT>();
result.value();
}
)cc");
}
TEST_P(UncheckedStatusOrAccessModelTest, PtrConstruct) {
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target() {
STATUSOR_VOIDPTR sor = nullptr;
*sor;
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target() {
STATUSOR_VOIDPTR sor(nullptr);
*sor;
}
)cc");
}
TEST_P(UncheckedStatusOrAccessModelTest, InPlaceConstruct) {
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target() {
STATUSOR_VOIDPTR absl_sor(absl::in_place, {nullptr});
*absl_sor;
STATUSOR_VOIDPTR std_sor(std::in_place, {nullptr});
*std_sor;
}
)cc");
}
TEST_P(UncheckedStatusOrAccessModelTest, ConstructStatusOrFromReference) {
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target() {
const auto sor1 = Make<STATUSOR_INT&>();
const auto sor2 = Make<STATUSOR_INT&>();
if (!sor1.ok() && !sor2.ok()) return;
if (sor1.ok() && !sor2.ok()) {
} else if (!sor1.ok() && sor2.ok()) {
} else {
sor1.value();
sor2.value();
}
}
)cc");
}
TEST_P(UncheckedStatusOrAccessModelTest, ConstructStatusFromReference) {
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
void target() {
const auto sor1 = Make<STATUSOR_INT&>();
const auto sor2 = Make<STATUSOR_INT&>();
const auto s1 = Make<STATUS&>();
const auto s2 = Make<STATUS&>();
if (!s1.ok() && !s2.ok()) return;
if (s1.ok() && !s2.ok()) {
} else if (!s1.ok() && s2.ok()) {
} else {
if (s1 != sor1.status() || s2 != sor2.status()) return;
sor1.value();
sor2.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