@@ -177,6 +177,41 @@ static auto isPointerComparisonOperatorCall(std::string operator_name) {
177177 pointee (anyOf (statusOrType (), statusType ())))))));
178178}
179179
180+ // The nullPointerConstant in the two matchers below is to support
181+ // absl::StatusOr<void*> X = nullptr.
182+ // nullptr does not match the bound type.
183+ // TODO: be less restrictive around convertible types in general.
184+ static auto isStatusOrValueAssignmentCall () {
185+ using namespace ::clang::ast_matchers; // NOLINT: Too many names
186+ return cxxOperatorCallExpr (
187+ hasOverloadedOperatorName (" =" ),
188+ callee (cxxMethodDecl (ofClass (statusOrClass ()))),
189+ hasArgument (1 , anyOf (hasType (hasUnqualifiedDesugaredType (
190+ type (equalsBoundNode (" T" )))),
191+ nullPointerConstant ())));
192+ }
193+
194+ static auto isStatusOrValueConstructor () {
195+ using namespace ::clang::ast_matchers; // NOLINT: Too many names
196+ return cxxConstructExpr (
197+ hasType (statusOrType ()),
198+ hasArgument (0 ,
199+ anyOf (hasType (hasCanonicalType (type (equalsBoundNode (" T" )))),
200+ nullPointerConstant (),
201+ hasType (namedDecl (hasAnyName (" absl::in_place_t" ,
202+ " std::in_place_t" ))))));
203+ }
204+
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+
180215static auto
181216buildDiagnoseMatchSwitch (const UncheckedStatusOrAccessModelOptions &Options) {
182217 return CFGMatchSwitchBuilder<const Environment,
@@ -528,6 +563,46 @@ static void transferEmplaceCall(const CXXMemberCallExpr *Expr,
528563 State.Env .assume (OkVal.formula ());
529564}
530565
566+ static void transferValueAssignmentCall (const CXXOperatorCallExpr *Expr,
567+ const MatchFinder::MatchResult &,
568+ LatticeTransferState &State) {
569+ assert (Expr->getNumArgs () > 1 );
570+
571+ auto *StatusOrLoc = State.Env .get <RecordStorageLocation>(*Expr->getArg (0 ));
572+ if (StatusOrLoc == nullptr )
573+ return ;
574+
575+ auto &OkVal = initializeStatusOr (*StatusOrLoc, State.Env );
576+ State.Env .assume (OkVal.formula ());
577+ }
578+
579+ static void transferValueConstructor (const CXXConstructExpr *Expr,
580+ const MatchFinder::MatchResult &,
581+ LatticeTransferState &State) {
582+ auto &OkVal =
583+ initializeStatusOr (State.Env .getResultObjectLocation (*Expr), State.Env );
584+ State.Env .assume (OkVal.formula ());
585+ }
586+
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+
531606CFGMatchSwitch<LatticeTransferState>
532607buildTransferMatchSwitch (ASTContext &Ctx,
533608 CFGMatchSwitchBuilder<LatticeTransferState> Builder) {
@@ -573,6 +648,20 @@ buildTransferMatchSwitch(ASTContext &Ctx,
573648 .CaseOfCFGStmt <CallExpr>(isNotOkStatusCall (), transferNotOkStatusCall)
574649 .CaseOfCFGStmt <CXXMemberCallExpr>(isStatusOrMemberCallWithName (" emplace" ),
575650 transferEmplaceCall)
651+ .CaseOfCFGStmt <CXXOperatorCallExpr>(isStatusOrValueAssignmentCall (),
652+ transferValueAssignmentCall)
653+ .CaseOfCFGStmt <CXXConstructExpr>(isStatusOrValueConstructor (),
654+ 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)
576665 .Build ();
577666}
578667
0 commit comments