diff --git a/clang/lib/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.cpp b/clang/lib/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.cpp index 1b68d704239e8..c917c8e8c11ba 100644 --- a/clang/lib/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.cpp +++ b/clang/lib/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.cpp @@ -1037,6 +1037,26 @@ transferAssertionResultOperatorBoolCall(const CXXMemberCallExpr *Expr, State.Env.setValue(*Expr, Res); } +static void transferDerefCall(const CallExpr *Expr, + const MatchFinder::MatchResult &, + LatticeTransferState &State) { + auto *StatusOrLoc = State.Env.get(*Expr->getArg(0)); + + if (StatusOrLoc && State.Env.getStorageLocation(*Expr) == nullptr) + State.Env.setStorageLocation(*Expr, + StatusOrLoc->getSyntheticField("value")); +} + +static void transferArrowCall(const CallExpr *Expr, + const MatchFinder::MatchResult &, + LatticeTransferState &State) { + auto *StatusOrLoc = State.Env.get(*Expr->getArg(0)); + if (!StatusOrLoc) + return; + State.Env.setValue(*Expr, State.Env.create( + StatusOrLoc->getSyntheticField("value"))); +} + static RecordStorageLocation * getSmartPtrLikeStorageLocation(const Expr &E, const Environment &Env) { if (!E.isPRValue()) @@ -1123,6 +1143,10 @@ buildTransferMatchSwitch(ASTContext &Ctx, transferValueAssignmentCall) .CaseOfCFGStmt(isStatusOrValueConstructor(), transferValueConstructor) + .CaseOfCFGStmt(isStatusOrOperatorCallWithName("->"), + transferArrowCall) + .CaseOfCFGStmt(isStatusOrOperatorCallWithName("*"), + transferDerefCall) .CaseOfCFGStmt(isAsStatusCallWithStatus(), transferAsStatusCallWithStatus) .CaseOfCFGStmt(isAsStatusCallWithStatusOr(), diff --git a/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp b/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp index 48e61abf09f19..cd7353c62f537 100644 --- a/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp +++ b/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp @@ -3691,6 +3691,155 @@ TEST_P(UncheckedStatusOrAccessModelTest, UniquePtrReset) { )cc"); } +TEST_P(UncheckedStatusOrAccessModelTest, NestedStatusOrInStatusOrStruct) { + // Non-standard assignment with a nested StatusOr. + ExpectDiagnosticsFor( + R"cc( +#include "unchecked_statusor_access_test_defs.h" + + struct Inner { + absl::StatusOr sor; + }; + + struct Outer { + absl::StatusOr inner; + }; + + void target() { + Outer foo = Make(); + foo.inner->sor = "a"; // [[unsafe]] + } + )cc"); + + ExpectDiagnosticsFor( + R"cc( +#include "unchecked_statusor_access_test_defs.h" + + struct Foo { + absl::StatusOr sor; + }; + + void target(const absl::StatusOr& foo) { + if (foo.ok() && foo->sor.ok()) foo->sor.value(); + } + )cc"); + + ExpectDiagnosticsFor( + R"cc( +#include "unchecked_statusor_access_test_defs.h" + + struct Foo { + absl::StatusOr sor; + }; + + void target(const absl::StatusOr& foo) { + if (foo.ok() && (*foo).sor.ok()) (*foo).sor.value(); + } + )cc"); + + // With assignment. + ExpectDiagnosticsFor( + R"cc( +#include "unchecked_statusor_access_test_defs.h" + + struct Foo { + absl::StatusOr sor; + }; + + void target(absl::StatusOr& foo) { + if (foo.ok() && foo->sor.ok()) { + foo->sor = Make>(); + foo->sor.value(); // [[unsafe]] + } + } + )cc"); + + ExpectDiagnosticsFor( + R"cc( +#include "unchecked_statusor_access_test_defs.h" + + struct Foo { + absl::StatusOr sor; + }; + + void target(absl::StatusOr& foo) { + if (foo.ok() && foo->sor.ok()) { + auto n = Make>(); + if (n.ok()) foo->sor = n; + foo->sor.value(); + } + } + )cc"); + + ExpectDiagnosticsFor( + R"cc( +#include "unchecked_statusor_access_test_defs.h" + + struct Foo { + absl::StatusOr sor; + }; + + void target(absl::StatusOr& foo) { + if (foo.ok() && foo->sor.ok()) { + auto n = Make>(); + if (n.ok()) foo->sor = std::move(n); + foo->sor.value(); + } + } + )cc"); + + ExpectDiagnosticsFor( + R"cc( +#include "unchecked_statusor_access_test_defs.h" + + struct Foo { + absl::StatusOr sor; + }; + + void target(absl::StatusOr& foo) { + if (foo.ok() && foo->sor.ok()) *foo->sor; + } + )cc"); + + ExpectDiagnosticsFor( + R"cc( +#include "unchecked_statusor_access_test_defs.h" + + struct Foo { + absl::StatusOr sor; + }; + + void target(absl::StatusOr& foo) { + if (!foo.ok()) return; + if (!foo->sor.ok()) + foo->sor.value(); // [[unsafe]] + else + foo->sor.value(); + } + )cc"); + + ExpectDiagnosticsFor( + R"cc( +#include "unchecked_statusor_access_test_defs.h" + + struct Foo { + absl::StatusOr sor; + }; + + void target(absl::StatusOr& foo, bool b) { + if (!foo.ok()) return; + if (b) { + if (!foo->sor.ok()) return; + foo->sor.value(); + } else { + if (!foo->sor.ok()) return; + foo->sor.value(); + } + foo->sor.value(); + } + )cc"); +} + } // namespace std::string