Skip to content

Commit d3a3b4c

Browse files
fmayerLukacma
authored andcommitted
[FlowSensitive] [StatusOr] [9/N] Make sure all StatusOr are initialized
This is important if the first use of a StatusOr (or Status) is in a conditional statement, we need a stable value for `ok` from outside of the conditional statement to make sure we don't use a different variable in every branch. Reviewers: jvoung, Xazax-hun Reviewed By: jvoung Pull Request: llvm#163898
1 parent f5de387 commit d3a3b4c

File tree

2 files changed

+78
-0
lines changed

2 files changed

+78
-0
lines changed

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

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,16 @@ static auto isStatusOrValueConstructor() {
202202
"std::in_place_t"))))));
203203
}
204204

205+
static auto isStatusOrConstructor() {
206+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
207+
return cxxConstructExpr(hasType(statusOrType()));
208+
}
209+
210+
static auto isStatusConstructor() {
211+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
212+
return cxxConstructExpr(hasType(statusType()));
213+
}
214+
205215
static auto
206216
buildDiagnoseMatchSwitch(const UncheckedStatusOrAccessModelOptions &Options) {
207217
return CFGMatchSwitchBuilder<const Environment,
@@ -574,6 +584,25 @@ static void transferValueConstructor(const CXXConstructExpr *Expr,
574584
State.Env.assume(OkVal.formula());
575585
}
576586

587+
static void transferStatusOrConstructor(const CXXConstructExpr *Expr,
588+
const MatchFinder::MatchResult &,
589+
LatticeTransferState &State) {
590+
RecordStorageLocation &StatusOrLoc = State.Env.getResultObjectLocation(*Expr);
591+
RecordStorageLocation &StatusLoc = locForStatus(StatusOrLoc);
592+
593+
if (State.Env.getValue(locForOk(StatusLoc)) == nullptr)
594+
initializeStatusOr(StatusOrLoc, State.Env);
595+
}
596+
597+
static void transferStatusConstructor(const CXXConstructExpr *Expr,
598+
const MatchFinder::MatchResult &,
599+
LatticeTransferState &State) {
600+
RecordStorageLocation &StatusLoc = State.Env.getResultObjectLocation(*Expr);
601+
602+
if (State.Env.getValue(locForOk(StatusLoc)) == nullptr)
603+
initializeStatus(StatusLoc, State.Env);
604+
}
605+
577606
CFGMatchSwitch<LatticeTransferState>
578607
buildTransferMatchSwitch(ASTContext &Ctx,
579608
CFGMatchSwitchBuilder<LatticeTransferState> Builder) {
@@ -623,6 +652,16 @@ buildTransferMatchSwitch(ASTContext &Ctx,
623652
transferValueAssignmentCall)
624653
.CaseOfCFGStmt<CXXConstructExpr>(isStatusOrValueConstructor(),
625654
transferValueConstructor)
655+
// N.B. These need to come after all other CXXConstructExpr.
656+
// These are there to make sure that every Status and StatusOr object
657+
// have their ok boolean initialized when constructed. If we were to
658+
// lazily initialize them when we first access them, we can produce
659+
// false positives if that first access is in a control flow statement.
660+
// You can comment out these two constructors and see tests fail.
661+
.CaseOfCFGStmt<CXXConstructExpr>(isStatusOrConstructor(),
662+
transferStatusOrConstructor)
663+
.CaseOfCFGStmt<CXXConstructExpr>(isStatusConstructor(),
664+
transferStatusConstructor)
626665
.Build();
627666
}
628667

clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3147,6 +3147,45 @@ TEST_P(UncheckedStatusOrAccessModelTest, InPlaceConstruct) {
31473147
)cc");
31483148
}
31493149

3150+
TEST_P(UncheckedStatusOrAccessModelTest, ConstructStatusOrFromReference) {
3151+
ExpectDiagnosticsFor(R"cc(
3152+
#include "unchecked_statusor_access_test_defs.h"
3153+
void target() {
3154+
const auto sor1 = Make<STATUSOR_INT&>();
3155+
const auto sor2 = Make<STATUSOR_INT&>();
3156+
if (!sor1.ok() && !sor2.ok()) return;
3157+
if (sor1.ok() && !sor2.ok()) {
3158+
} else if (!sor1.ok() && sor2.ok()) {
3159+
} else {
3160+
sor1.value();
3161+
sor2.value();
3162+
}
3163+
}
3164+
)cc");
3165+
}
3166+
3167+
TEST_P(UncheckedStatusOrAccessModelTest, ConstructStatusFromReference) {
3168+
ExpectDiagnosticsFor(R"cc(
3169+
#include "unchecked_statusor_access_test_defs.h"
3170+
3171+
void target() {
3172+
const auto sor1 = Make<STATUSOR_INT&>();
3173+
const auto sor2 = Make<STATUSOR_INT&>();
3174+
const auto s1 = Make<STATUS&>();
3175+
const auto s2 = Make<STATUS&>();
3176+
3177+
if (!s1.ok() && !s2.ok()) return;
3178+
if (s1.ok() && !s2.ok()) {
3179+
} else if (!s1.ok() && s2.ok()) {
3180+
} else {
3181+
if (s1 != sor1.status() || s2 != sor2.status()) return;
3182+
sor1.value();
3183+
sor2.value();
3184+
}
3185+
}
3186+
)cc");
3187+
}
3188+
31503189
} // namespace
31513190

31523191
std::string

0 commit comments

Comments
 (0)