Skip to content

Commit c19e0b2

Browse files
authored
[StatusOr] [10/N] Add support for absl CHECK macros (#169749)
1 parent f02dc4d commit c19e0b2

File tree

2 files changed

+190
-0
lines changed

2 files changed

+190
-0
lines changed

clang/lib/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.cpp

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,31 @@ static auto isStatusConstructor() {
211211
using namespace ::clang::ast_matchers; // NOLINT: Too many names
212212
return cxxConstructExpr(hasType(statusType()));
213213
}
214+
static auto isLoggingGetReferenceableValueCall() {
215+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
216+
return callExpr(callee(
217+
functionDecl(hasName("::absl::log_internal::GetReferenceableValue"))));
218+
}
219+
220+
static auto isLoggingCheckEqImpl() {
221+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
222+
return callExpr(
223+
callee(functionDecl(hasName("::absl::log_internal::Check_EQImpl"))));
224+
}
225+
226+
static auto isAsStatusCallWithStatus() {
227+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
228+
return callExpr(
229+
callee(functionDecl(hasName("::absl::log_internal::AsStatus"))),
230+
hasArgument(0, hasType(statusClass())));
231+
}
232+
233+
static auto isAsStatusCallWithStatusOr() {
234+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
235+
return callExpr(
236+
callee(functionDecl(hasName("::absl::log_internal::AsStatus"))),
237+
hasArgument(0, hasType(statusOrType())));
238+
}
214239

215240
static auto
216241
buildDiagnoseMatchSwitch(const UncheckedStatusOrAccessModelOptions &Options) {
@@ -602,6 +627,76 @@ static void transferStatusConstructor(const CXXConstructExpr *Expr,
602627
if (State.Env.getValue(locForOk(StatusLoc)) == nullptr)
603628
initializeStatus(StatusLoc, State.Env);
604629
}
630+
static void
631+
transferLoggingGetReferenceableValueCall(const CallExpr *Expr,
632+
const MatchFinder::MatchResult &,
633+
LatticeTransferState &State) {
634+
assert(Expr->getNumArgs() == 1);
635+
if (Expr->getArg(0)->isPRValue())
636+
return;
637+
auto *ArgLoc = State.Env.getStorageLocation(*Expr->getArg(0));
638+
if (ArgLoc == nullptr)
639+
return;
640+
641+
State.Env.setStorageLocation(*Expr, *ArgLoc);
642+
}
643+
644+
static void transferLoggingCheckEqImpl(const CallExpr *Expr,
645+
const MatchFinder::MatchResult &,
646+
LatticeTransferState &State) {
647+
assert(Expr->getNumArgs() > 2);
648+
649+
auto *EqVal = evaluateEquality(Expr->getArg(0), Expr->getArg(1), State.Env);
650+
if (EqVal == nullptr)
651+
return;
652+
653+
// Consider modelling this more accurately instead of assigning BoolValue
654+
// as the value of an expression of pointer type.
655+
// For now, this is being handled in transferPointerToBoolean.
656+
State.Env.setValue(*Expr, State.Env.makeNot(*EqVal));
657+
}
658+
659+
static void transferAsStatusCallWithStatus(const CallExpr *Expr,
660+
const MatchFinder::MatchResult &,
661+
LatticeTransferState &State) {
662+
assert(Expr->getNumArgs() == 1);
663+
664+
auto *ArgLoc = State.Env.get<RecordStorageLocation>(*Expr->getArg(0));
665+
if (ArgLoc == nullptr)
666+
return;
667+
668+
if (State.Env.getValue(locForOk(*ArgLoc)) == nullptr)
669+
initializeStatus(*ArgLoc, State.Env);
670+
671+
auto &ExprVal = State.Env.create<PointerValue>(*ArgLoc);
672+
State.Env.setValue(*Expr, ExprVal);
673+
}
674+
675+
static void transferAsStatusCallWithStatusOr(const CallExpr *Expr,
676+
const MatchFinder::MatchResult &,
677+
LatticeTransferState &State) {
678+
assert(Expr->getNumArgs() == 1);
679+
680+
auto *ArgLoc = State.Env.get<RecordStorageLocation>(*Expr->getArg(0));
681+
if (ArgLoc == nullptr)
682+
return;
683+
684+
RecordStorageLocation &StatusLoc = locForStatus(*ArgLoc);
685+
686+
if (State.Env.getValue(locForOk(StatusLoc)) == nullptr)
687+
initializeStatusOr(*ArgLoc, State.Env);
688+
689+
auto &ExprVal = State.Env.create<PointerValue>(StatusLoc);
690+
State.Env.setValue(*Expr, ExprVal);
691+
}
692+
693+
static void transferPointerToBoolean(const ImplicitCastExpr *Expr,
694+
const MatchFinder::MatchResult &,
695+
LatticeTransferState &State) {
696+
if (auto *SubExprVal =
697+
dyn_cast_or_null<BoolValue>(State.Env.getValue(*Expr->getSubExpr())))
698+
State.Env.setValue(*Expr, *SubExprVal);
699+
}
605700

606701
CFGMatchSwitch<LatticeTransferState>
607702
buildTransferMatchSwitch(ASTContext &Ctx,
@@ -652,6 +747,14 @@ buildTransferMatchSwitch(ASTContext &Ctx,
652747
transferValueAssignmentCall)
653748
.CaseOfCFGStmt<CXXConstructExpr>(isStatusOrValueConstructor(),
654749
transferValueConstructor)
750+
.CaseOfCFGStmt<CallExpr>(isAsStatusCallWithStatus(),
751+
transferAsStatusCallWithStatus)
752+
.CaseOfCFGStmt<CallExpr>(isAsStatusCallWithStatusOr(),
753+
transferAsStatusCallWithStatusOr)
754+
.CaseOfCFGStmt<CallExpr>(isLoggingGetReferenceableValueCall(),
755+
transferLoggingGetReferenceableValueCall)
756+
.CaseOfCFGStmt<CallExpr>(isLoggingCheckEqImpl(),
757+
transferLoggingCheckEqImpl)
655758
// N.B. These need to come after all other CXXConstructExpr.
656759
// These are there to make sure that every Status and StatusOr object
657760
// have their ok boolean initialized when constructed. If we were to
@@ -662,6 +765,9 @@ buildTransferMatchSwitch(ASTContext &Ctx,
662765
transferStatusOrConstructor)
663766
.CaseOfCFGStmt<CXXConstructExpr>(isStatusConstructor(),
664767
transferStatusConstructor)
768+
.CaseOfCFGStmt<ImplicitCastExpr>(
769+
implicitCastExpr(hasCastKind(CK_PointerToBoolean)),
770+
transferPointerToBoolean)
665771
.Build();
666772
}
667773

clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1725,6 +1725,90 @@ TEST_P(UncheckedStatusOrAccessModelTest, QcheckMacro) {
17251725
)cc");
17261726
}
17271727

1728+
TEST_P(UncheckedStatusOrAccessModelTest, CheckOkMacro) {
1729+
ExpectDiagnosticsFor(R"cc(
1730+
#include "unchecked_statusor_access_test_defs.h"
1731+
1732+
void target(STATUSOR_INT sor) {
1733+
CHECK_OK(sor.status());
1734+
sor.value();
1735+
}
1736+
)cc");
1737+
ExpectDiagnosticsFor(R"cc(
1738+
#include "unchecked_statusor_access_test_defs.h"
1739+
1740+
void target(STATUSOR_INT sor) {
1741+
CHECK_OK(sor);
1742+
sor.value();
1743+
}
1744+
)cc");
1745+
ExpectDiagnosticsFor(R"cc(
1746+
#include "unchecked_statusor_access_test_defs.h"
1747+
1748+
void target() {
1749+
STATUS s = Make<STATUS>();
1750+
CHECK_OK(s);
1751+
}
1752+
)cc");
1753+
}
1754+
1755+
TEST_P(UncheckedStatusOrAccessModelTest, QcheckOkMacro) {
1756+
ExpectDiagnosticsFor(R"cc(
1757+
#include "unchecked_statusor_access_test_defs.h"
1758+
1759+
void target(STATUSOR_INT sor) {
1760+
QCHECK_OK(sor.status());
1761+
sor.value();
1762+
}
1763+
)cc");
1764+
ExpectDiagnosticsFor(R"cc(
1765+
#include "unchecked_statusor_access_test_defs.h"
1766+
1767+
void target(STATUSOR_INT sor) {
1768+
QCHECK_OK(sor);
1769+
sor.value();
1770+
}
1771+
)cc");
1772+
ExpectDiagnosticsFor(R"cc(
1773+
#include "unchecked_statusor_access_test_defs.h"
1774+
1775+
void target() {
1776+
STATUS s = Make<STATUS>();
1777+
QCHECK_OK(s);
1778+
}
1779+
)cc");
1780+
}
1781+
1782+
TEST_P(UncheckedStatusOrAccessModelTest, CheckEqMacro) {
1783+
ExpectDiagnosticsFor(R"cc(
1784+
#include "unchecked_statusor_access_test_defs.h"
1785+
1786+
void target(STATUSOR_INT sor) {
1787+
CHECK_EQ(sor.status(), absl::OkStatus());
1788+
sor.value();
1789+
}
1790+
)cc");
1791+
ExpectDiagnosticsFor(R"cc(
1792+
#include "unchecked_statusor_access_test_defs.h"
1793+
1794+
void target() {
1795+
CHECK_EQ(Make<STATUS>(), absl::OkStatus());
1796+
CHECK_EQ(absl::OkStatus(), Make<STATUS>());
1797+
}
1798+
)cc");
1799+
}
1800+
1801+
TEST_P(UncheckedStatusOrAccessModelTest, QcheckEqMacro) {
1802+
ExpectDiagnosticsFor(R"cc(
1803+
#include "unchecked_statusor_access_test_defs.h"
1804+
1805+
void target(STATUSOR_INT sor) {
1806+
QCHECK_EQ(sor.status(), absl::OkStatus());
1807+
sor.value();
1808+
}
1809+
)cc");
1810+
}
1811+
17281812
TEST_P(UncheckedStatusOrAccessModelTest, CheckNeMacro) {
17291813
ExpectDiagnosticsFor(R"cc(
17301814
#include "unchecked_statusor_access_test_defs.h"

0 commit comments

Comments
 (0)