Skip to content

Commit fe38c52

Browse files
fmayergithub-actions[bot]
authored andcommitted
Automerge: [FlowSensitive] [StatusOr] [13/N] Add support for gtest ASSERTs
Reviewers: jvoung Reviewed By: jvoung Pull Request: llvm/llvm-project#170947
2 parents 609ad41 + d34c717 commit fe38c52

File tree

2 files changed

+444
-0
lines changed

2 files changed

+444
-0
lines changed

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

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,76 @@ static auto isNonConstMemberOperatorCall() {
281281
return cxxOperatorCallExpr(callee(cxxMethodDecl(unless(isConst()))));
282282
}
283283

284+
static auto isMakePredicateFormatterFromIsOkMatcherCall() {
285+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
286+
return callExpr(
287+
callee(functionDecl(
288+
hasName("::testing::internal::MakePredicateFormatterFromMatcher"))),
289+
hasArgument(
290+
0, hasType(cxxRecordDecl(hasAnyName(
291+
"::testing::status::internal_status::IsOkMatcher",
292+
"::absl_testing::status_internal::IsOkMatcher",
293+
"::testing::status::internal_status::IsOkAndHoldsMatcher",
294+
"::absl_testing::status_internal::IsOkAndHoldsMatcher")))));
295+
}
296+
297+
static auto isStatusIsOkMatcherCall() {
298+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
299+
return callExpr(callee(functionDecl(hasAnyName(
300+
"::testing::status::StatusIs", "absl_testing::StatusIs",
301+
"::testing::status::CanonicalStatusIs",
302+
"::absl_testing::CanonicalStatusIs"))),
303+
hasArgument(0, declRefExpr(to(enumConstantDecl(hasAnyName(
304+
"::absl::StatusCode::kOk", "OK"))))));
305+
}
306+
307+
static auto isMakePredicateFormatterFromStatusIsMatcherCall() {
308+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
309+
return callExpr(
310+
callee(functionDecl(
311+
hasName("::testing::internal::MakePredicateFormatterFromMatcher"))),
312+
hasArgument(0, hasType(cxxRecordDecl(hasAnyName(
313+
"::testing::status::internal_status::StatusIsMatcher",
314+
"::testing::status::internal_status::"
315+
"CanonicalStatusIsMatcher",
316+
"::absl_testing::status_internal::StatusIsMatcher",
317+
"::absl_testing::status_internal::"
318+
"CanonicalStatusIsMatcher")))));
319+
}
320+
321+
static auto isPredicateFormatterFromStatusMatcherCall() {
322+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
323+
return cxxOperatorCallExpr(
324+
hasOverloadedOperatorName("()"),
325+
callee(cxxMethodDecl(ofClass(
326+
hasName("testing::internal::PredicateFormatterFromMatcher")))),
327+
hasArgument(2, hasType(statusType())));
328+
}
329+
330+
static auto isPredicateFormatterFromStatusOrMatcherCall() {
331+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
332+
return cxxOperatorCallExpr(
333+
hasOverloadedOperatorName("()"),
334+
callee(cxxMethodDecl(ofClass(
335+
hasName("testing::internal::PredicateFormatterFromMatcher")))),
336+
hasArgument(2, hasType(statusOrType())));
337+
}
338+
339+
static auto isAssertionResultOperatorBoolCall() {
340+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
341+
return cxxMemberCallExpr(
342+
on(expr(unless(cxxThisExpr()))),
343+
callee(cxxMethodDecl(hasName("operator bool"),
344+
ofClass(hasName("testing::AssertionResult")))));
345+
}
346+
347+
static auto isAssertionResultConstructFromBoolCall() {
348+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
349+
return cxxConstructExpr(
350+
hasType(recordDecl(hasName("testing::AssertionResult"))),
351+
hasArgument(0, hasType(booleanType())));
352+
}
353+
284354
static auto
285355
buildDiagnoseMatchSwitch(const UncheckedStatusOrAccessModelOptions &Options) {
286356
return CFGMatchSwitchBuilder<const Environment,
@@ -365,13 +435,39 @@ bool isStatusType(QualType Type) {
365435
return isTypeNamed(Type, {"absl"}, "Status");
366436
}
367437

438+
static bool isPredicateFormatterFromMatcherType(QualType Type) {
439+
return isTypeNamed(Type, {"testing", "internal"},
440+
"PredicateFormatterFromMatcher");
441+
}
442+
443+
static bool isAssertionResultType(QualType Type) {
444+
return isTypeNamed(Type, {"testing"}, "AssertionResult");
445+
}
446+
447+
static bool isStatusIsMatcherType(QualType Type) {
448+
return isTypeNamed(Type, {"testing", "status", "internal_status"},
449+
"StatusIsMatcher") ||
450+
isTypeNamed(Type, {"testing", "status", "internal_status"},
451+
"CanonicalStatusIsMatcher") ||
452+
isTypeNamed(Type, {"absl_testing", "status_internal"},
453+
"StatusIsMatcher") ||
454+
isTypeNamed(Type, {"absl_testing", "status_internal"},
455+
"CanonicalStatusIsMatcher");
456+
}
457+
368458
llvm::StringMap<QualType> getSyntheticFields(QualType Ty, QualType StatusType,
369459
const CXXRecordDecl &RD) {
370460
if (auto *TRD = getStatusOrBaseClass(Ty))
371461
return {{"status", StatusType}, {"value", getStatusOrValueType(TRD)}};
372462
if (isStatusType(Ty) || (RD.hasDefinition() &&
373463
RD.isDerivedFrom(StatusType->getAsCXXRecordDecl())))
374464
return {{"ok", RD.getASTContext().BoolTy}};
465+
if (isAssertionResultType(Ty))
466+
return {{"ok", RD.getASTContext().BoolTy}};
467+
if (isPredicateFormatterFromMatcherType(Ty))
468+
return {{"ok_predicate", RD.getASTContext().BoolTy}};
469+
if (isStatusIsMatcherType(Ty))
470+
return {{"ok_matcher", RD.getASTContext().BoolTy}};
375471
return {};
376472
}
377473

@@ -388,6 +484,13 @@ BoolValue &valForOk(RecordStorageLocation &StatusLoc, Environment &Env) {
388484
return *Val;
389485
return initializeStatus(StatusLoc, Env);
390486
}
487+
static StorageLocation &locForOkPredicate(RecordStorageLocation &StatusLoc) {
488+
return StatusLoc.getSyntheticField("ok_predicate");
489+
}
490+
491+
static StorageLocation &locForOkMatcher(RecordStorageLocation &StatusLoc) {
492+
return StatusLoc.getSyntheticField("ok_matcher");
493+
}
391494

392495
static void transferStatusOrOkCall(const CXXMemberCallExpr *Expr,
393496
const MatchFinder::MatchResult &,
@@ -850,6 +953,97 @@ transferNonConstMemberOperatorCall(const CXXOperatorCallExpr *Expr,
850953
handleNonConstMemberCall(Expr, RecordLoc, Result, State);
851954
}
852955

956+
static void transferMakePredicateFormatterFromIsOkMatcherCall(
957+
const CallExpr *Expr, const MatchFinder::MatchResult &,
958+
LatticeTransferState &State) {
959+
State.Env.setValue(
960+
locForOkPredicate(State.Env.getResultObjectLocation(*Expr)),
961+
State.Env.getBoolLiteralValue(true));
962+
}
963+
964+
static void transferStatusIsOkMatcherCall(const CallExpr *Expr,
965+
const MatchFinder::MatchResult &,
966+
LatticeTransferState &State) {
967+
BoolValue &OkMatcherVal = State.Env.getBoolLiteralValue(true);
968+
State.Env.setValue(locForOkMatcher(State.Env.getResultObjectLocation(*Expr)),
969+
OkMatcherVal);
970+
}
971+
972+
static void transferMakePredicateFormatterFromStatusIsMatcherCall(
973+
const CallExpr *Expr, const MatchFinder::MatchResult &,
974+
LatticeTransferState &State) {
975+
assert(Expr->isPRValue());
976+
auto &Loc = State.Env.getResultObjectLocation(*Expr->getArg(0));
977+
auto &OkMatcherLoc = locForOkMatcher(Loc);
978+
BoolValue *OkMatcherVal = State.Env.get<BoolValue>(OkMatcherLoc);
979+
if (OkMatcherVal == nullptr)
980+
return;
981+
State.Env.setValue(
982+
locForOkPredicate(State.Env.getResultObjectLocation(*Expr)),
983+
*OkMatcherVal);
984+
}
985+
986+
static void
987+
transferPredicateFormatterMatcherCall(const CXXOperatorCallExpr *Expr,
988+
LatticeTransferState &State,
989+
bool IsStatusOr) {
990+
auto *Loc = State.Env.get<RecordStorageLocation>(*Expr->getArg(0));
991+
if (Loc == nullptr)
992+
return;
993+
994+
auto *ObjectLoc = State.Env.get<RecordStorageLocation>(*Expr->getArg(2));
995+
if (ObjectLoc == nullptr)
996+
return;
997+
998+
auto &OkPredicateLoc = locForOkPredicate(*Loc);
999+
BoolValue *OkPredicateVal = State.Env.get<BoolValue>(OkPredicateLoc);
1000+
if (OkPredicateVal == nullptr)
1001+
return;
1002+
1003+
if (IsStatusOr)
1004+
ObjectLoc = &locForStatus(*ObjectLoc);
1005+
auto &StatusOk = valForOk(*ObjectLoc, State.Env);
1006+
1007+
auto &A = State.Env.arena();
1008+
auto &Res = State.Env.makeAtomicBoolValue();
1009+
State.Env.assume(
1010+
A.makeImplies(OkPredicateVal->formula(),
1011+
A.makeEquals(StatusOk.formula(), Res.formula())));
1012+
State.Env.setValue(locForOk(State.Env.getResultObjectLocation(*Expr)), Res);
1013+
}
1014+
1015+
static void
1016+
transferAssertionResultConstructFromBoolCall(const CXXConstructExpr *Expr,
1017+
const MatchFinder::MatchResult &,
1018+
LatticeTransferState &State) {
1019+
assert(Expr->getNumArgs() > 0);
1020+
1021+
auto *StatusAdaptorLoc = State.Env.get<StorageLocation>(*Expr->getArg(0));
1022+
if (StatusAdaptorLoc == nullptr)
1023+
return;
1024+
BoolValue *OkVal = State.Env.get<BoolValue>(*StatusAdaptorLoc);
1025+
if (OkVal == nullptr)
1026+
return;
1027+
State.Env.setValue(locForOk(State.Env.getResultObjectLocation(*Expr)),
1028+
*OkVal);
1029+
}
1030+
1031+
static void
1032+
transferAssertionResultOperatorBoolCall(const CXXMemberCallExpr *Expr,
1033+
const MatchFinder::MatchResult &,
1034+
LatticeTransferState &State) {
1035+
auto *RecordLoc = getImplicitObjectLocation(*Expr, State.Env);
1036+
if (RecordLoc == nullptr)
1037+
return;
1038+
BoolValue *OkVal = State.Env.get<BoolValue>(locForOk(*RecordLoc));
1039+
if (OkVal == nullptr)
1040+
return;
1041+
auto &A = State.Env.arena();
1042+
auto &Res = State.Env.makeAtomicBoolValue();
1043+
State.Env.assume(A.makeEquals(OkVal->formula(), Res.formula()));
1044+
State.Env.setValue(*Expr, Res);
1045+
}
1046+
8531047
static RecordStorageLocation *
8541048
getSmartPtrLikeStorageLocation(const Expr &E, const Environment &Env) {
8551049
if (!E.isPRValue())
@@ -865,6 +1059,33 @@ buildTransferMatchSwitch(ASTContext &Ctx,
8651059
CFGMatchSwitchBuilder<LatticeTransferState> Builder) {
8661060
using namespace ::clang::ast_matchers; // NOLINT: Too many names
8671061
return std::move(Builder)
1062+
.CaseOfCFGStmt<CallExpr>(
1063+
isMakePredicateFormatterFromIsOkMatcherCall(),
1064+
transferMakePredicateFormatterFromIsOkMatcherCall)
1065+
.CaseOfCFGStmt<CallExpr>(isStatusIsOkMatcherCall(),
1066+
transferStatusIsOkMatcherCall)
1067+
.CaseOfCFGStmt<CallExpr>(
1068+
isMakePredicateFormatterFromStatusIsMatcherCall(),
1069+
transferMakePredicateFormatterFromStatusIsMatcherCall)
1070+
.CaseOfCFGStmt<CXXOperatorCallExpr>(
1071+
isPredicateFormatterFromStatusOrMatcherCall(),
1072+
[](const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &,
1073+
LatticeTransferState &State) {
1074+
transferPredicateFormatterMatcherCall(Expr, State,
1075+
/*IsStatusOr=*/true);
1076+
})
1077+
.CaseOfCFGStmt<CXXOperatorCallExpr>(
1078+
isPredicateFormatterFromStatusMatcherCall(),
1079+
[](const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &,
1080+
LatticeTransferState &State) {
1081+
transferPredicateFormatterMatcherCall(Expr, State,
1082+
/*IsStatusOr=*/false);
1083+
})
1084+
.CaseOfCFGStmt<CXXConstructExpr>(
1085+
isAssertionResultConstructFromBoolCall(),
1086+
transferAssertionResultConstructFromBoolCall)
1087+
.CaseOfCFGStmt<CXXMemberCallExpr>(isAssertionResultOperatorBoolCall(),
1088+
transferAssertionResultOperatorBoolCall)
8681089
.CaseOfCFGStmt<CXXMemberCallExpr>(isStatusOrMemberCallWithName("ok"),
8691090
transferStatusOrOkCall)
8701091
.CaseOfCFGStmt<CXXMemberCallExpr>(isStatusOrMemberCallWithName("status"),

0 commit comments

Comments
 (0)