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 @@ -211,6 +211,31 @@ static auto isStatusConstructor() {
using namespace ::clang::ast_matchers; // NOLINT: Too many names
return cxxConstructExpr(hasType(statusType()));
}
static auto isLoggingGetReferenceableValueCall() {
using namespace ::clang::ast_matchers; // NOLINT: Too many names
return callExpr(callee(
functionDecl(hasName("::absl::log_internal::GetReferenceableValue"))));
}

static auto isLoggingCheckEqImpl() {
using namespace ::clang::ast_matchers; // NOLINT: Too many names
return callExpr(
callee(functionDecl(hasName("::absl::log_internal::Check_EQImpl"))));
}

static auto isAsStatusCallWithStatus() {
using namespace ::clang::ast_matchers; // NOLINT: Too many names
return callExpr(
callee(functionDecl(hasName("::absl::log_internal::AsStatus"))),
hasArgument(0, hasType(statusClass())));
}

static auto isAsStatusCallWithStatusOr() {
using namespace ::clang::ast_matchers; // NOLINT: Too many names
return callExpr(
callee(functionDecl(hasName("::absl::log_internal::AsStatus"))),
hasArgument(0, hasType(statusOrType())));
}

static auto
buildDiagnoseMatchSwitch(const UncheckedStatusOrAccessModelOptions &Options) {
Expand Down Expand Up @@ -602,6 +627,67 @@ static void transferStatusConstructor(const CXXConstructExpr *Expr,
if (State.Env.getValue(locForOk(StatusLoc)) == nullptr)
initializeStatus(StatusLoc, State.Env);
}
static void
transferLoggingGetReferenceableValueCall(const CallExpr *Expr,
const MatchFinder::MatchResult &,
LatticeTransferState &State) {
assert(Expr->getNumArgs() == 1);
if (Expr->getArg(0)->isPRValue())
return;
auto *ArgLoc = State.Env.getStorageLocation(*Expr->getArg(0));
if (ArgLoc == nullptr)
return;

State.Env.setStorageLocation(*Expr, *ArgLoc);
}

static void transferLoggingCheckEqImpl(const CallExpr *Expr,
const MatchFinder::MatchResult &,
LatticeTransferState &State) {
assert(Expr->getNumArgs() > 2);

auto *EqVal = evaluateEquality(Expr->getArg(0), Expr->getArg(1), State.Env);
if (EqVal == nullptr)
return;

// TODO(sgatev): Model pointer nullability more accurately instead of
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the TODO be updated? (not sgatev ?)

Might also be good to explain a bit more what's going on here, e.g., to explain why the makeNot (the EQImpl returns a char* and when equal is ... nullptr?)

// assigning BoolValue as the value of an expression of pointer type.
State.Env.setValue(*Expr, State.Env.makeNot(*EqVal));
}

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

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

if (State.Env.getValue(locForOk(*ArgLoc)) == nullptr)
initializeStatus(*ArgLoc, State.Env);

auto &ExprVal = State.Env.create<PointerValue>(*ArgLoc);
State.Env.setValue(*Expr, ExprVal);
}

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

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

RecordStorageLocation &StatusLoc = locForStatus(*ArgLoc);

if (State.Env.getValue(locForOk(StatusLoc)) == nullptr)
initializeStatusOr(*ArgLoc, State.Env);

auto &ExprVal = State.Env.create<PointerValue>(StatusLoc);
State.Env.setValue(*Expr, ExprVal);
}

CFGMatchSwitch<LatticeTransferState>
buildTransferMatchSwitch(ASTContext &Ctx,
Expand Down Expand Up @@ -652,6 +738,14 @@ buildTransferMatchSwitch(ASTContext &Ctx,
transferValueAssignmentCall)
.CaseOfCFGStmt<CXXConstructExpr>(isStatusOrValueConstructor(),
transferValueConstructor)
.CaseOfCFGStmt<CallExpr>(isAsStatusCallWithStatus(),
transferAsStatusCallWithStatus)
.CaseOfCFGStmt<CallExpr>(isAsStatusCallWithStatusOr(),
transferAsStatusCallWithStatusOr)
.CaseOfCFGStmt<CallExpr>(isLoggingGetReferenceableValueCall(),
transferLoggingGetReferenceableValueCall)
.CaseOfCFGStmt<CallExpr>(isLoggingCheckEqImpl(),
transferLoggingCheckEqImpl)
// N.B. These need to come after all other CXXConstructExpr.
// These are there to make sure that every Status and StatusOr object
// have their ok boolean initialized when constructed. If we were to
Expand All @@ -662,6 +756,14 @@ buildTransferMatchSwitch(ASTContext &Ctx,
transferStatusOrConstructor)
.CaseOfCFGStmt<CXXConstructExpr>(isStatusConstructor(),
transferStatusConstructor)
.CaseOfCFGStmt<ImplicitCastExpr>(
implicitCastExpr(hasCastKind(CK_PointerToBoolean)),
[](const ImplicitCastExpr *Expr, const MatchFinder::MatchResult &,
LatticeTransferState &State) {
if (auto *SubExprVal = dyn_cast_or_null<BoolValue>(
State.Env.getValue(*Expr->getSubExpr())))
State.Env.setValue(*Expr, *SubExprVal);
})
.Build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1725,6 +1725,90 @@ TEST_P(UncheckedStatusOrAccessModelTest, QcheckMacro) {
)cc");
}

TEST_P(UncheckedStatusOrAccessModelTest, CheckOkMacro) {
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"

void target(STATUSOR_INT sor) {
CHECK_OK(sor.status());
sor.value();
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"

void target(STATUSOR_INT sor) {
CHECK_OK(sor);
sor.value();
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"

void target() {
STATUS s = Make<STATUS>();
CHECK_OK(s);
}
)cc");
}

TEST_P(UncheckedStatusOrAccessModelTest, QcheckOkMacro) {
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"

void target(STATUSOR_INT sor) {
QCHECK_OK(sor.status());
sor.value();
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"

void target(STATUSOR_INT sor) {
QCHECK_OK(sor);
sor.value();
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"

void target() {
STATUS s = Make<STATUS>();
QCHECK_OK(s);
}
)cc");
}

TEST_P(UncheckedStatusOrAccessModelTest, CheckEqMacro) {
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"

void target(STATUSOR_INT sor) {
CHECK_EQ(sor.status(), absl::OkStatus());
sor.value();
}
)cc");
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"

void target() {
CHECK_EQ(Make<STATUS>(), absl::OkStatus());
CHECK_EQ(absl::OkStatus(), Make<STATUS>());
}
)cc");
}

TEST_P(UncheckedStatusOrAccessModelTest, QcheckEqMacro) {
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"

void target(STATUSOR_INT sor) {
QCHECK_EQ(sor.status(), absl::OkStatus());
sor.value();
}
)cc");
}

TEST_P(UncheckedStatusOrAccessModelTest, CheckNeMacro) {
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
Expand Down