Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -241,9 +241,9 @@ auto nulloptTypeDecl() {
auto hasNulloptType() { return hasType(nulloptTypeDecl()); }

auto inPlaceClass() {
return recordDecl(hasAnyName("std::in_place_t", "absl::in_place_t",
"base::in_place_t", "folly::in_place_t",
"bsl::in_place_t"));
return namedDecl(hasAnyName("std::in_place_t", "absl::in_place_t",
"base::in_place_t", "folly::in_place_t",
"bsl::in_place_t"));
}

auto isOptionalNulloptConstructor() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,27 @@ static auto isPointerComparisonOperatorCall(std::string operator_name) {
hasType(hasCanonicalType(pointerType(pointee(statusType())))))));
}

static auto isStatusOrValueAssignmentCall() {
using namespace ::clang::ast_matchers; // NOLINT: Too many names
return cxxOperatorCallExpr(
hasOverloadedOperatorName("="),
callee(cxxMethodDecl(ofClass(statusOrClass()))),
hasArgument(1, anyOf(hasType(hasUnqualifiedDesugaredType(
type(equalsBoundNode("T")))),
nullPointerConstant())));
}

static auto isStatusOrValueConstructor() {
using namespace ::clang::ast_matchers; // NOLINT: Too many names
return cxxConstructExpr(
hasType(statusOrType()),
hasArgument(0,
anyOf(hasType(hasCanonicalType(type(equalsBoundNode("T")))),
nullPointerConstant(),
hasType(namedDecl(hasAnyName("absl::in_place_t",
"std::in_place_t"))))));
}

static auto
buildDiagnoseMatchSwitch(const UncheckedStatusOrAccessModelOptions &Options) {
return CFGMatchSwitchBuilder<const Environment,
Expand Down Expand Up @@ -537,6 +558,27 @@ static void transferEmplaceCall(const CXXMemberCallExpr *Expr,
State.Env.assume(OkVal.formula());
}

static void transferValueAssignmentCall(const CXXOperatorCallExpr *Expr,
const MatchFinder::MatchResult &,
LatticeTransferState &State) {
assert(Expr->getNumArgs() > 1);

auto *StatusOrLoc = State.Env.get<RecordStorageLocation>(*Expr->getArg(0));
if (StatusOrLoc == nullptr)
return;

auto &OkVal = initializeStatusOr(*StatusOrLoc, State.Env);
State.Env.assume(OkVal.formula());
}

static void transferValueConstructor(const CXXConstructExpr *Expr,
const MatchFinder::MatchResult &,
LatticeTransferState &State) {
auto &OkVal =
initializeStatusOr(State.Env.getResultObjectLocation(*Expr), State.Env);
State.Env.assume(OkVal.formula());
}

CFGMatchSwitch<LatticeTransferState>
buildTransferMatchSwitch(ASTContext &Ctx,
CFGMatchSwitchBuilder<LatticeTransferState> Builder) {
Expand Down Expand Up @@ -582,6 +624,10 @@ buildTransferMatchSwitch(ASTContext &Ctx,
.CaseOfCFGStmt<CallExpr>(isNotOkStatusCall(), transferNotOkStatusCall)
.CaseOfCFGStmt<CXXMemberCallExpr>(isStatusOrMemberCallWithName("emplace"),
transferEmplaceCall)
.CaseOfCFGStmt<CXXOperatorCallExpr>(isStatusOrValueAssignmentCall(),
transferValueAssignmentCall)
.CaseOfCFGStmt<CXXConstructExpr>(isStatusOrValueConstructor(),
transferValueConstructor)
.Build();
}

Expand Down
12 changes: 6 additions & 6 deletions clang/unittests/Analysis/FlowSensitive/MockHeaders.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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() {}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2963,6 +2963,178 @@ TEST_P(UncheckedStatusOrAccessModelTest, Emplace) {
)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");
}

} // namespace

std::string
Expand Down
Loading