Skip to content

Commit e603fac

Browse files
authored
[FlowSensitive] [StatusOr] [15/15] Support references to Status(Or) ptrs
That hopefully concludes the initial upstreaming. Reviewers: jvoung Reviewed By: jvoung Pull Request: #170951
1 parent 6b75eae commit e603fac

File tree

2 files changed

+115
-0
lines changed

2 files changed

+115
-0
lines changed

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

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,25 @@ static auto isAssertionResultConstructFromBoolCall() {
351351
hasArgument(0, hasType(booleanType())));
352352
}
353353

354+
static auto isStatusOrReturningCall() {
355+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
356+
return callExpr(
357+
callee(functionDecl(returns(possiblyReferencedStatusOrType()))));
358+
}
359+
360+
static auto isStatusOrPtrReturningCall() {
361+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
362+
return callExpr(callee(functionDecl(returns(hasUnqualifiedDesugaredType(
363+
pointerType(pointee(possiblyReferencedStatusOrType())))))));
364+
}
365+
366+
static auto isStatusPtrReturningCall() {
367+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
368+
return callExpr(callee(functionDecl(returns(hasUnqualifiedDesugaredType(
369+
pointerType(pointee(hasUnqualifiedDesugaredType(
370+
recordType(hasDeclaration(statusClass()))))))))));
371+
}
372+
354373
static auto
355374
buildDiagnoseMatchSwitch(const UncheckedStatusOrAccessModelOptions &Options) {
356375
return CFGMatchSwitchBuilder<const Environment,
@@ -1074,6 +1093,40 @@ static void transferValueCall(const CXXMemberCallExpr *Expr,
10741093
StatusOrLoc->getSyntheticField("value"));
10751094
}
10761095

1096+
static void transferStatusOrPtrReturningCall(const CallExpr *Expr,
1097+
const MatchFinder::MatchResult &,
1098+
LatticeTransferState &State) {
1099+
PointerValue *PointerVal =
1100+
dyn_cast_or_null<PointerValue>(State.Env.getValue(*Expr));
1101+
if (!PointerVal) {
1102+
PointerVal = cast<PointerValue>(State.Env.createValue(Expr->getType()));
1103+
State.Env.setValue(*Expr, *PointerVal);
1104+
}
1105+
1106+
auto *RecordLoc =
1107+
dyn_cast_or_null<RecordStorageLocation>(&PointerVal->getPointeeLoc());
1108+
if (RecordLoc != nullptr &&
1109+
State.Env.getValue(locForOk(locForStatus(*RecordLoc))) == nullptr)
1110+
initializeStatusOr(*RecordLoc, State.Env);
1111+
}
1112+
1113+
static void transferStatusPtrReturningCall(const CallExpr *Expr,
1114+
const MatchFinder::MatchResult &,
1115+
LatticeTransferState &State) {
1116+
PointerValue *PointerVal =
1117+
dyn_cast_or_null<PointerValue>(State.Env.getValue(*Expr));
1118+
if (!PointerVal) {
1119+
PointerVal = cast<PointerValue>(State.Env.createValue(Expr->getType()));
1120+
State.Env.setValue(*Expr, *PointerVal);
1121+
}
1122+
1123+
auto *RecordLoc =
1124+
dyn_cast_or_null<RecordStorageLocation>(&PointerVal->getPointeeLoc());
1125+
if (RecordLoc != nullptr &&
1126+
State.Env.getValue(locForOk(*RecordLoc)) == nullptr)
1127+
initializeStatus(*RecordLoc, State.Env);
1128+
}
1129+
10771130
static RecordStorageLocation *
10781131
getSmartPtrLikeStorageLocation(const Expr &E, const Environment &Env) {
10791132
if (!E.isPRValue())
@@ -1228,6 +1281,18 @@ buildTransferMatchSwitch(ASTContext &Ctx,
12281281
transferNonConstMemberCall)
12291282
.CaseOfCFGStmt<CXXOperatorCallExpr>(isNonConstMemberOperatorCall(),
12301283
transferNonConstMemberOperatorCall)
1284+
// N.B. this has to be after transferConstMemberCall, otherwise we would
1285+
// always return a fresh RecordStorageLocation for the StatusOr.
1286+
.CaseOfCFGStmt<CallExpr>(isStatusOrReturningCall(),
1287+
[](const CallExpr *Expr,
1288+
const MatchFinder::MatchResult &,
1289+
LatticeTransferState &State) {
1290+
transferStatusOrReturningCall(Expr, State);
1291+
})
1292+
.CaseOfCFGStmt<CallExpr>(isStatusOrPtrReturningCall(),
1293+
transferStatusOrPtrReturningCall)
1294+
.CaseOfCFGStmt<CallExpr>(isStatusPtrReturningCall(),
1295+
transferStatusPtrReturningCall)
12311296
// N.B. These need to come after all other CXXConstructExpr.
12321297
// These are there to make sure that every Status and StatusOr object
12331298
// have their ok boolean initialized when constructed. If we were to

clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3910,6 +3910,56 @@ TEST_P(UncheckedStatusOrAccessModelTest, NestedStatusOrInStatusOrStruct) {
39103910
)cc");
39113911
}
39123912

3913+
TEST_P(UncheckedStatusOrAccessModelTest, StatusOrPtrReference) {
3914+
ExpectDiagnosticsFor(R"cc(
3915+
#include "unchecked_statusor_access_test_defs.h"
3916+
3917+
const STATUSOR_INT* foo();
3918+
3919+
void target() {
3920+
const auto& sor = foo();
3921+
if (sor->ok()) sor->value();
3922+
}
3923+
)cc");
3924+
3925+
ExpectDiagnosticsFor(R"cc(
3926+
#include "unchecked_statusor_access_test_defs.h"
3927+
3928+
using StatusOrPtr = const STATUSOR_INT*;
3929+
StatusOrPtr foo();
3930+
3931+
void target() {
3932+
const auto& sor = foo();
3933+
if (sor->ok()) sor->value();
3934+
}
3935+
)cc");
3936+
}
3937+
3938+
TEST_P(UncheckedStatusOrAccessModelTest, StatusPtrReference) {
3939+
ExpectDiagnosticsFor(R"cc(
3940+
#include "unchecked_statusor_access_test_defs.h"
3941+
3942+
const STATUS* foo();
3943+
3944+
void target(STATUSOR_INT sor) {
3945+
const auto& s = foo();
3946+
if (s->ok() && *s == sor.status()) sor.value();
3947+
}
3948+
)cc");
3949+
3950+
ExpectDiagnosticsFor(R"cc(
3951+
#include "unchecked_statusor_access_test_defs.h"
3952+
3953+
using StatusPtr = const STATUS*;
3954+
StatusPtr foo();
3955+
3956+
void target(STATUSOR_INT sor) {
3957+
const auto& s = foo();
3958+
if (s->ok() && *s == sor.status()) sor.value();
3959+
}
3960+
)cc");
3961+
}
3962+
39133963
} // namespace
39143964

39153965
std::string

0 commit comments

Comments
 (0)