@@ -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+
284354static auto
285355buildDiagnoseMatchSwitch (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+
368458llvm::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
392495static 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+
8531047static RecordStorageLocation *
8541048getSmartPtrLikeStorageLocation (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