@@ -137,6 +137,37 @@ 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+
140171static auto
141172buildDiagnoseMatchSwitch (const UncheckedStatusOrAccessModelOptions &Options) {
142173 return CFGMatchSwitchBuilder<const Environment,
@@ -312,6 +343,118 @@ static void transferStatusUpdateCall(const CXXMemberCallExpr *Expr,
312343 State.Env .setValue (locForOk (*ThisLoc), NewVal);
313344}
314345
346+ static BoolValue *evaluateStatusEquality (RecordStorageLocation &LhsStatusLoc,
347+ RecordStorageLocation &RhsStatusLoc,
348+ Environment &Env) {
349+ auto &A = Env.arena ();
350+ // Logically, a Status object is composed of an error code that could take one
351+ // of multiple possible values, including the "ok" value. We track whether a
352+ // Status object has an "ok" value and represent this as an `ok` bit. Equality
353+ // of Status objects compares their error codes. Therefore, merely comparing
354+ // the `ok` bits isn't sufficient: when two Status objects are assigned non-ok
355+ // error codes the equality of their respective error codes matters. Since we
356+ // only track the `ok` bits, we can't make any conclusions about equality when
357+ // we know that two Status objects have non-ok values.
358+
359+ auto &LhsOkVal = valForOk (LhsStatusLoc, Env);
360+ auto &RhsOkVal = valForOk (RhsStatusLoc, Env);
361+
362+ auto &Res = Env.makeAtomicBoolValue ();
363+
364+ // lhs && rhs => res (a.k.a. !res => !lhs || !rhs)
365+ Env.assume (A.makeImplies (A.makeAnd (LhsOkVal.formula (), RhsOkVal.formula ()),
366+ Res.formula ()));
367+ // res => (lhs == rhs)
368+ Env.assume (A.makeImplies (
369+ Res.formula (), A.makeEquals (LhsOkVal.formula (), RhsOkVal.formula ())));
370+
371+ return &Res;
372+ }
373+
374+ static BoolValue *
375+ evaluateStatusOrEquality (RecordStorageLocation &LhsStatusOrLoc,
376+ RecordStorageLocation &RhsStatusOrLoc,
377+ Environment &Env) {
378+ auto &A = Env.arena ();
379+ // Logically, a StatusOr<T> object is composed of two values - a Status and a
380+ // value of type T. Equality of StatusOr objects compares both values.
381+ // Therefore, merely comparing the `ok` bits of the Status values isn't
382+ // sufficient. When two StatusOr objects are engaged, the equality of their
383+ // respective values of type T matters. Similarly, when two StatusOr objects
384+ // have Status values that have non-ok error codes, the equality of the error
385+ // codes matters. Since we only track the `ok` bits of the Status values, we
386+ // can't make any conclusions about equality when we know that two StatusOr
387+ // objects are engaged or when their Status values contain non-ok error codes.
388+ auto &LhsOkVal = valForOk (locForStatus (LhsStatusOrLoc), Env);
389+ auto &RhsOkVal = valForOk (locForStatus (RhsStatusOrLoc), Env);
390+ auto &res = Env.makeAtomicBoolValue ();
391+
392+ // res => (lhs == rhs)
393+ Env.assume (A.makeImplies (
394+ res.formula (), A.makeEquals (LhsOkVal.formula (), RhsOkVal.formula ())));
395+ return &res;
396+ }
397+
398+ static BoolValue *evaluateEquality (const Expr *LhsExpr, const Expr *RhsExpr,
399+ Environment &Env) {
400+ // Check the type of both sides in case an operator== is added that admits
401+ // different types.
402+ if (isStatusOrType (LhsExpr->getType ()) &&
403+ isStatusOrType (RhsExpr->getType ())) {
404+ auto *LhsStatusOrLoc = Env.get <RecordStorageLocation>(*LhsExpr);
405+ if (LhsStatusOrLoc == nullptr )
406+ return nullptr ;
407+ auto *RhsStatusOrLoc = Env.get <RecordStorageLocation>(*RhsExpr);
408+ if (RhsStatusOrLoc == nullptr )
409+ return nullptr ;
410+
411+ return evaluateStatusOrEquality (*LhsStatusOrLoc, *RhsStatusOrLoc, Env);
412+ }
413+ if (isStatusType (LhsExpr->getType ()) && isStatusType (RhsExpr->getType ())) {
414+ auto *LhsStatusLoc = Env.get <RecordStorageLocation>(*LhsExpr);
415+ if (LhsStatusLoc == nullptr )
416+ return nullptr ;
417+
418+ auto *RhsStatusLoc = Env.get <RecordStorageLocation>(*RhsExpr);
419+ if (RhsStatusLoc == nullptr )
420+ return nullptr ;
421+
422+ return evaluateStatusEquality (*LhsStatusLoc, *RhsStatusLoc, Env);
423+ }
424+ return nullptr ;
425+ }
426+
427+ static void transferComparisonOperator (const CXXOperatorCallExpr *Expr,
428+ LatticeTransferState &State,
429+ bool IsNegative) {
430+ auto *LhsAndRhsVal =
431+ evaluateEquality (Expr->getArg (0 ), Expr->getArg (1 ), State.Env );
432+ if (LhsAndRhsVal == nullptr )
433+ return ;
434+
435+ if (IsNegative)
436+ State.Env .setValue (*Expr, State.Env .makeNot (*LhsAndRhsVal));
437+ else
438+ State.Env .setValue (*Expr, *LhsAndRhsVal);
439+ }
440+
441+ static void transferOkStatusCall (const CallExpr *Expr,
442+ const MatchFinder::MatchResult &,
443+ LatticeTransferState &State) {
444+ auto &OkVal =
445+ initializeStatus (State.Env .getResultObjectLocation (*Expr), State.Env );
446+ State.Env .assume (OkVal.formula ());
447+ }
448+
449+ static void transferNotOkStatusCall (const CallExpr *Expr,
450+ const MatchFinder::MatchResult &,
451+ LatticeTransferState &State) {
452+ auto &OkVal =
453+ initializeStatus (State.Env .getResultObjectLocation (*Expr), State.Env );
454+ auto &A = State.Env .arena ();
455+ State.Env .assume (A.makeNot (OkVal.formula ()));
456+ }
457+
315458CFGMatchSwitch<LatticeTransferState>
316459buildTransferMatchSwitch (ASTContext &Ctx,
317460 CFGMatchSwitchBuilder<LatticeTransferState> Builder) {
@@ -325,6 +468,22 @@ buildTransferMatchSwitch(ASTContext &Ctx,
325468 transferStatusOkCall)
326469 .CaseOfCFGStmt <CXXMemberCallExpr>(isStatusMemberCallWithName (" Update" ),
327470 transferStatusUpdateCall)
471+ .CaseOfCFGStmt <CXXOperatorCallExpr>(
472+ isComparisonOperatorCall (" ==" ),
473+ [](const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &,
474+ LatticeTransferState &State) {
475+ transferComparisonOperator (Expr, State,
476+ /* IsNegative=*/ false );
477+ })
478+ .CaseOfCFGStmt <CXXOperatorCallExpr>(
479+ isComparisonOperatorCall (" !=" ),
480+ [](const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &,
481+ LatticeTransferState &State) {
482+ transferComparisonOperator (Expr, State,
483+ /* IsNegative=*/ true );
484+ })
485+ .CaseOfCFGStmt <CallExpr>(isOkStatusCall (), transferOkStatusCall)
486+ .CaseOfCFGStmt <CallExpr>(isNotOkStatusCall (), transferNotOkStatusCall)
328487 .Build ();
329488}
330489
0 commit comments