@@ -137,6 +137,80 @@ static auto valueOperatorCall() {
137137 isStatusOrOperatorCallWithName (" ->" )));
138138}
139139
140+ static clang::ast_matchers::TypeMatcher statusType () {
141+ using namespace ::clang::ast_matchers; // NOLINT: Too many names
142+ return hasCanonicalType (qualType (hasDeclaration (statusClass ())));
143+ }
144+
145+ static auto isComparisonOperatorCall (llvm::StringRef operator_name) {
146+ using namespace ::clang::ast_matchers; // NOLINT: Too many names
147+ return cxxOperatorCallExpr (
148+ hasOverloadedOperatorName (operator_name), argumentCountIs (2 ),
149+ hasArgument (0 , anyOf (hasType (statusType ()), hasType (statusOrType ()))),
150+ hasArgument (1 , anyOf (hasType (statusType ()), hasType (statusOrType ()))));
151+ }
152+
153+ static auto isOkStatusCall () {
154+ using namespace ::clang::ast_matchers; // NOLINT: Too many names
155+ return callExpr (callee (functionDecl (hasName (" ::absl::OkStatus" ))));
156+ }
157+
158+ static auto isNotOkStatusCall () {
159+ using namespace ::clang::ast_matchers; // NOLINT: Too many names
160+ return callExpr (callee (functionDecl (hasAnyName (
161+ " ::absl::AbortedError" , " ::absl::AlreadyExistsError" ,
162+ " ::absl::CancelledError" , " ::absl::DataLossError" ,
163+ " ::absl::DeadlineExceededError" , " ::absl::FailedPreconditionError" ,
164+ " ::absl::InternalError" , " ::absl::InvalidArgumentError" ,
165+ " ::absl::NotFoundError" , " ::absl::OutOfRangeError" ,
166+ " ::absl::PermissionDeniedError" , " ::absl::ResourceExhaustedError" ,
167+ " ::absl::UnauthenticatedError" , " ::absl::UnavailableError" ,
168+ " ::absl::UnimplementedError" , " ::absl::UnknownError" ))));
169+ }
170+
171+ static auto isPointerComparisonOperatorCall (std::string operator_name) {
172+ using namespace ::clang::ast_matchers; // NOLINT: Too many names
173+ return binaryOperator (
174+ hasOperatorName (operator_name),
175+ hasLHS (
176+ anyOf (hasType (hasCanonicalType (pointerType (pointee (statusOrType ())))),
177+ hasType (hasCanonicalType (pointerType (pointee (statusType ())))))),
178+ hasRHS (anyOf (
179+ hasType (hasCanonicalType (pointerType (pointee (statusOrType ())))),
180+ hasType (hasCanonicalType (pointerType (pointee (statusType ())))))));
181+ }
182+
183+ static auto isStatusOrValueAssignmentCall () {
184+ using namespace ::clang::ast_matchers; // NOLINT: Too many names
185+ return cxxOperatorCallExpr (
186+ hasOverloadedOperatorName (" =" ),
187+ callee (cxxMethodDecl (ofClass (statusOrClass ()))),
188+ hasArgument (1 , anyOf (hasType (hasUnqualifiedDesugaredType (
189+ type (equalsBoundNode (" T" )))),
190+ nullPointerConstant ())));
191+ }
192+
193+ static auto isStatusOrValueConstructor () {
194+ using namespace ::clang::ast_matchers; // NOLINT: Too many names
195+ return cxxConstructExpr (
196+ hasType (statusOrType ()),
197+ hasArgument (0 ,
198+ anyOf (hasType (hasCanonicalType (type (equalsBoundNode (" T" )))),
199+ nullPointerConstant (),
200+ hasType (namedDecl (hasAnyName (" absl::in_place_t" ,
201+ " std::in_place_t" ))))));
202+ }
203+
204+ static auto isStatusOrConstructor () {
205+ using namespace ::clang::ast_matchers; // NOLINT: Too many names
206+ return cxxConstructExpr (hasType (statusOrType ()));
207+ }
208+
209+ static auto isStatusConstructor () {
210+ using namespace ::clang::ast_matchers; // NOLINT: Too many names
211+ return cxxConstructExpr (hasType (statusType ()));
212+ }
213+
140214static auto
141215buildDiagnoseMatchSwitch (const UncheckedStatusOrAccessModelOptions &Options) {
142216 return CFGMatchSwitchBuilder<const Environment,
@@ -312,6 +386,222 @@ static void transferStatusUpdateCall(const CXXMemberCallExpr *Expr,
312386 State.Env .setValue (locForOk (*ThisLoc), NewVal);
313387}
314388
389+ static BoolValue *evaluateStatusEquality (RecordStorageLocation &LhsStatusLoc,
390+ RecordStorageLocation &RhsStatusLoc,
391+ Environment &Env) {
392+ auto &A = Env.arena ();
393+ // Logically, a Status object is composed of an error code that could take one
394+ // of multiple possible values, including the "ok" value. We track whether a
395+ // Status object has an "ok" value and represent this as an `ok` bit. Equality
396+ // of Status objects compares their error codes. Therefore, merely comparing
397+ // the `ok` bits isn't sufficient: when two Status objects are assigned non-ok
398+ // error codes the equality of their respective error codes matters. Since we
399+ // only track the `ok` bits, we can't make any conclusions about equality when
400+ // we know that two Status objects have non-ok values.
401+
402+ auto &LhsOkVal = valForOk (LhsStatusLoc, Env);
403+ auto &RhsOkVal = valForOk (RhsStatusLoc, Env);
404+
405+ auto &Res = Env.makeAtomicBoolValue ();
406+
407+ // lhs && rhs => res (a.k.a. !res => !lhs || !rhs)
408+ Env.assume (A.makeImplies (A.makeAnd (LhsOkVal.formula (), RhsOkVal.formula ()),
409+ Res.formula ()));
410+ // res => (lhs == rhs)
411+ Env.assume (A.makeImplies (
412+ Res.formula (), A.makeEquals (LhsOkVal.formula (), RhsOkVal.formula ())));
413+
414+ return &Res;
415+ }
416+
417+ static BoolValue *
418+ evaluateStatusOrEquality (RecordStorageLocation &LhsStatusOrLoc,
419+ RecordStorageLocation &RhsStatusOrLoc,
420+ Environment &Env) {
421+ auto &A = Env.arena ();
422+ // Logically, a StatusOr<T> object is composed of two values - a Status and a
423+ // value of type T. Equality of StatusOr objects compares both values.
424+ // Therefore, merely comparing the `ok` bits of the Status values isn't
425+ // sufficient. When two StatusOr objects are engaged, the equality of their
426+ // respective values of type T matters. Similarly, when two StatusOr objects
427+ // have Status values that have non-ok error codes, the equality of the error
428+ // codes matters. Since we only track the `ok` bits of the Status values, we
429+ // can't make any conclusions about equality when we know that two StatusOr
430+ // objects are engaged or when their Status values contain non-ok error codes.
431+ auto &LhsOkVal = valForOk (locForStatus (LhsStatusOrLoc), Env);
432+ auto &RhsOkVal = valForOk (locForStatus (RhsStatusOrLoc), Env);
433+ auto &res = Env.makeAtomicBoolValue ();
434+
435+ // res => (lhs == rhs)
436+ Env.assume (A.makeImplies (
437+ res.formula (), A.makeEquals (LhsOkVal.formula (), RhsOkVal.formula ())));
438+ return &res;
439+ }
440+
441+ static BoolValue *evaluateEquality (const Expr *LhsExpr, const Expr *RhsExpr,
442+ Environment &Env) {
443+ // Check the type of both sides in case an operator== is added that admits
444+ // different types.
445+ if (isStatusOrType (LhsExpr->getType ()) &&
446+ isStatusOrType (RhsExpr->getType ())) {
447+ auto *LhsStatusOrLoc = Env.get <RecordStorageLocation>(*LhsExpr);
448+ if (LhsStatusOrLoc == nullptr )
449+ return nullptr ;
450+ auto *RhsStatusOrLoc = Env.get <RecordStorageLocation>(*RhsExpr);
451+ if (RhsStatusOrLoc == nullptr )
452+ return nullptr ;
453+
454+ return evaluateStatusOrEquality (*LhsStatusOrLoc, *RhsStatusOrLoc, Env);
455+ }
456+ if (isStatusType (LhsExpr->getType ()) && isStatusType (RhsExpr->getType ())) {
457+ auto *LhsStatusLoc = Env.get <RecordStorageLocation>(*LhsExpr);
458+ if (LhsStatusLoc == nullptr )
459+ return nullptr ;
460+
461+ auto *RhsStatusLoc = Env.get <RecordStorageLocation>(*RhsExpr);
462+ if (RhsStatusLoc == nullptr )
463+ return nullptr ;
464+
465+ return evaluateStatusEquality (*LhsStatusLoc, *RhsStatusLoc, Env);
466+ }
467+ return nullptr ;
468+ }
469+
470+ static void transferComparisonOperator (const CXXOperatorCallExpr *Expr,
471+ LatticeTransferState &State,
472+ bool IsNegative) {
473+ auto *LhsAndRhsVal =
474+ evaluateEquality (Expr->getArg (0 ), Expr->getArg (1 ), State.Env );
475+ if (LhsAndRhsVal == nullptr )
476+ return ;
477+
478+ if (IsNegative)
479+ State.Env .setValue (*Expr, State.Env .makeNot (*LhsAndRhsVal));
480+ else
481+ State.Env .setValue (*Expr, *LhsAndRhsVal);
482+ }
483+
484+ static RecordStorageLocation *getPointeeLocation (const Expr &Expr,
485+ Environment &Env) {
486+ if (auto *PointerVal = Env.get <PointerValue>(Expr))
487+ return dyn_cast<RecordStorageLocation>(&PointerVal->getPointeeLoc ());
488+ return nullptr ;
489+ }
490+
491+ static BoolValue *evaluatePointerEquality (const Expr *LhsExpr,
492+ const Expr *RhsExpr,
493+ Environment &Env) {
494+ assert (LhsExpr->getType ()->isPointerType ());
495+ assert (RhsExpr->getType ()->isPointerType ());
496+ RecordStorageLocation *LhsStatusLoc = nullptr ;
497+ RecordStorageLocation *RhsStatusLoc = nullptr ;
498+ if (isStatusOrType (LhsExpr->getType ()->getPointeeType ()) &&
499+ isStatusOrType (RhsExpr->getType ()->getPointeeType ())) {
500+ auto *LhsStatusOrLoc = getPointeeLocation (*LhsExpr, Env);
501+ auto *RhsStatusOrLoc = getPointeeLocation (*RhsExpr, Env);
502+ if (LhsStatusOrLoc == nullptr || RhsStatusOrLoc == nullptr )
503+ return nullptr ;
504+ LhsStatusLoc = &locForStatus (*LhsStatusOrLoc);
505+ RhsStatusLoc = &locForStatus (*RhsStatusOrLoc);
506+ } else if (isStatusType (LhsExpr->getType ()->getPointeeType ()) &&
507+ isStatusType (RhsExpr->getType ()->getPointeeType ())) {
508+ LhsStatusLoc = getPointeeLocation (*LhsExpr, Env);
509+ RhsStatusLoc = getPointeeLocation (*RhsExpr, Env);
510+ }
511+ if (LhsStatusLoc == nullptr || RhsStatusLoc == nullptr )
512+ return nullptr ;
513+ auto &LhsOkVal = valForOk (*LhsStatusLoc, Env);
514+ auto &RhsOkVal = valForOk (*RhsStatusLoc, Env);
515+ auto &Res = Env.makeAtomicBoolValue ();
516+ auto &A = Env.arena ();
517+ Env.assume (A.makeImplies (
518+ Res.formula (), A.makeEquals (LhsOkVal.formula (), RhsOkVal.formula ())));
519+ return &Res;
520+ }
521+
522+ static void transferPointerComparisonOperator (const BinaryOperator *Expr,
523+ LatticeTransferState &State,
524+ bool IsNegative) {
525+ auto *LhsAndRhsVal =
526+ evaluatePointerEquality (Expr->getLHS (), Expr->getRHS (), State.Env );
527+ if (LhsAndRhsVal == nullptr )
528+ return ;
529+
530+ if (IsNegative)
531+ State.Env .setValue (*Expr, State.Env .makeNot (*LhsAndRhsVal));
532+ else
533+ State.Env .setValue (*Expr, *LhsAndRhsVal);
534+ }
535+
536+ static void transferOkStatusCall (const CallExpr *Expr,
537+ const MatchFinder::MatchResult &,
538+ LatticeTransferState &State) {
539+ auto &OkVal =
540+ initializeStatus (State.Env .getResultObjectLocation (*Expr), State.Env );
541+ State.Env .assume (OkVal.formula ());
542+ }
543+
544+ static void transferNotOkStatusCall (const CallExpr *Expr,
545+ const MatchFinder::MatchResult &,
546+ LatticeTransferState &State) {
547+ auto &OkVal =
548+ initializeStatus (State.Env .getResultObjectLocation (*Expr), State.Env );
549+ auto &A = State.Env .arena ();
550+ State.Env .assume (A.makeNot (OkVal.formula ()));
551+ }
552+
553+ static void transferEmplaceCall (const CXXMemberCallExpr *Expr,
554+ const MatchFinder::MatchResult &,
555+ LatticeTransferState &State) {
556+ RecordStorageLocation *StatusOrLoc =
557+ getImplicitObjectLocation (*Expr, State.Env );
558+ if (StatusOrLoc == nullptr )
559+ return ;
560+
561+ auto &OkVal = valForOk (locForStatus (*StatusOrLoc), State.Env );
562+ State.Env .assume (OkVal.formula ());
563+ }
564+
565+ static void transferValueAssignmentCall (const CXXOperatorCallExpr *Expr,
566+ const MatchFinder::MatchResult &,
567+ LatticeTransferState &State) {
568+ assert (Expr->getNumArgs () > 1 );
569+
570+ auto *StatusOrLoc = State.Env .get <RecordStorageLocation>(*Expr->getArg (0 ));
571+ if (StatusOrLoc == nullptr )
572+ return ;
573+
574+ auto &OkVal = initializeStatusOr (*StatusOrLoc, State.Env );
575+ State.Env .assume (OkVal.formula ());
576+ }
577+
578+ static void transferValueConstructor (const CXXConstructExpr *Expr,
579+ const MatchFinder::MatchResult &,
580+ LatticeTransferState &State) {
581+ auto &OkVal =
582+ initializeStatusOr (State.Env .getResultObjectLocation (*Expr), State.Env );
583+ State.Env .assume (OkVal.formula ());
584+ }
585+
586+ static void transferStatusOrConstructor (const CXXConstructExpr *Expr,
587+ const MatchFinder::MatchResult &,
588+ LatticeTransferState &State) {
589+ RecordStorageLocation &StatusOrLoc = State.Env .getResultObjectLocation (*Expr);
590+ RecordStorageLocation &StatusLoc = locForStatus (StatusOrLoc);
591+
592+ if (State.Env .getValue (locForOk (StatusLoc)) == nullptr )
593+ initializeStatusOr (StatusOrLoc, State.Env );
594+ }
595+
596+ static void transferStatusConstructor (const CXXConstructExpr *Expr,
597+ const MatchFinder::MatchResult &,
598+ LatticeTransferState &State) {
599+ RecordStorageLocation &StatusLoc = State.Env .getResultObjectLocation (*Expr);
600+
601+ if (State.Env .getValue (locForOk (StatusLoc)) == nullptr )
602+ initializeStatus (StatusLoc, State.Env );
603+ }
604+
315605CFGMatchSwitch<LatticeTransferState>
316606buildTransferMatchSwitch (ASTContext &Ctx,
317607 CFGMatchSwitchBuilder<LatticeTransferState> Builder) {
@@ -325,6 +615,52 @@ buildTransferMatchSwitch(ASTContext &Ctx,
325615 transferStatusOkCall)
326616 .CaseOfCFGStmt <CXXMemberCallExpr>(isStatusMemberCallWithName (" Update" ),
327617 transferStatusUpdateCall)
618+ .CaseOfCFGStmt <CXXOperatorCallExpr>(
619+ isComparisonOperatorCall (" ==" ),
620+ [](const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &,
621+ LatticeTransferState &State) {
622+ transferComparisonOperator (Expr, State,
623+ /* IsNegative=*/ false );
624+ })
625+ .CaseOfCFGStmt <CXXOperatorCallExpr>(
626+ isComparisonOperatorCall (" !=" ),
627+ [](const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &,
628+ LatticeTransferState &State) {
629+ transferComparisonOperator (Expr, State,
630+ /* IsNegative=*/ true );
631+ })
632+ .CaseOfCFGStmt <BinaryOperator>(
633+ isPointerComparisonOperatorCall (" ==" ),
634+ [](const BinaryOperator *Expr, const MatchFinder::MatchResult &,
635+ LatticeTransferState &State) {
636+ transferPointerComparisonOperator (Expr, State,
637+ /* IsNegative=*/ false );
638+ })
639+ .CaseOfCFGStmt <BinaryOperator>(
640+ isPointerComparisonOperatorCall (" !=" ),
641+ [](const BinaryOperator *Expr, const MatchFinder::MatchResult &,
642+ LatticeTransferState &State) {
643+ transferPointerComparisonOperator (Expr, State,
644+ /* IsNegative=*/ true );
645+ })
646+ .CaseOfCFGStmt <CallExpr>(isOkStatusCall (), transferOkStatusCall)
647+ .CaseOfCFGStmt <CallExpr>(isNotOkStatusCall (), transferNotOkStatusCall)
648+ .CaseOfCFGStmt <CXXMemberCallExpr>(isStatusOrMemberCallWithName (" emplace" ),
649+ transferEmplaceCall)
650+ .CaseOfCFGStmt <CXXOperatorCallExpr>(isStatusOrValueAssignmentCall (),
651+ transferValueAssignmentCall)
652+ .CaseOfCFGStmt <CXXConstructExpr>(isStatusOrValueConstructor (),
653+ transferValueConstructor)
654+ // N.B. These need to come after all other CXXConstructExpr.
655+ // These are there to make sure that every Status and StatusOr object
656+ // have their ok boolean initialized when constructed. If we were to
657+ // lazily initialize them when we first access them, we can produce
658+ // false positives if that first access is in a control flow statement.
659+ // You can comment out these two constructors and see tests fail.
660+ .CaseOfCFGStmt <CXXConstructExpr>(isStatusOrConstructor (),
661+ transferStatusOrConstructor)
662+ .CaseOfCFGStmt <CXXConstructExpr>(isStatusConstructor (),
663+ transferStatusConstructor)
328664 .Build ();
329665}
330666
0 commit comments