diff --git a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp --- a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp +++ b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp @@ -45,6 +45,11 @@ auto hasOptionalType() { return hasType(optionalClass()); } +auto hasOptionalOrAliasType() { + return hasUnqualifiedDesugaredType( + recordType(hasDeclaration(optionalClass()))); +} + auto isOptionalMemberCallWithName( llvm::StringRef MemberName, llvm::Optional Ignorable = llvm::None) { @@ -158,6 +163,12 @@ ComparesToSame(integerLiteral(equals(0))))); } +auto isCallReturningOptional() { + return callExpr(callee(functionDecl( + returns(anyOf(hasOptionalOrAliasType(), + referenceType(pointee(hasOptionalOrAliasType()))))))); +} + /// Creates a symbolic value for an `optional` value using `HasValueVal` as the /// symbolic value of its "has_value" property. StructValue &createOptionalValue(Environment &Env, BoolValue &HasValueVal) { @@ -322,6 +333,18 @@ }); } +void transferCallReturningOptional(const CallExpr *E, + const MatchFinder::MatchResult &Result, + LatticeTransferState &State) { + if (State.Env.getStorageLocation(*E, SkipPast::None) != nullptr) + return; + + auto &Loc = State.Env.createStorageLocation(*E); + State.Env.setStorageLocation(*E, Loc); + State.Env.setValue( + Loc, createOptionalValue(State.Env, State.Env.makeAtomicBoolValue())); +} + void assignOptionalValue(const Expr &E, LatticeTransferState &State, BoolValue &HasValueVal) { if (auto *OptionalLoc = @@ -547,6 +570,10 @@ // opt.value_or(X) != X .CaseOf(isValueOrNotEqX(), transferValueOrNotEqX) + // returns optional + .CaseOf(isCallReturningOptional(), + transferCallReturningOptional) + .Build(); } diff --git a/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp b/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp --- a/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp @@ -2150,6 +2150,70 @@ UnorderedElementsAre(Pair("check-1", "safe"), Pair("check-2", "safe"))); } +TEST_P(UncheckedOptionalAccessTest, CallReturningOptional) { + ExpectLatticeChecksFor( + R"( + #include "unchecked_optional_access_test.h" + + $ns::$optional MakeOpt(); + + void target() { + $ns::$optional opt = 0; + opt = MakeOpt(); + opt.value(); + /*[[check-1]]*/ + } + )", + UnorderedElementsAre(Pair("check-1", "unsafe: input.cc:9:7"))); + + ExpectLatticeChecksFor( + R"( + #include "unchecked_optional_access_test.h" + + const $ns::$optional& MakeOpt(); + + void target() { + $ns::$optional opt = 0; + opt = MakeOpt(); + opt.value(); + /*[[check-2]]*/ + } + )", + UnorderedElementsAre(Pair("check-2", "unsafe: input.cc:9:7"))); + + ExpectLatticeChecksFor( + R"( + #include "unchecked_optional_access_test.h" + + using IntOpt = $ns::$optional; + IntOpt MakeOpt(); + + void target() { + IntOpt opt = 0; + opt = MakeOpt(); + opt.value(); + /*[[check-3]]*/ + } + )", + UnorderedElementsAre(Pair("check-3", "unsafe: input.cc:10:7"))); + + ExpectLatticeChecksFor( + R"( + #include "unchecked_optional_access_test.h" + + using IntOpt = $ns::$optional; + const IntOpt& MakeOpt(); + + void target() { + IntOpt opt = 0; + opt = MakeOpt(); + opt.value(); + /*[[check-4]]*/ + } + )", + UnorderedElementsAre(Pair("check-4", "unsafe: input.cc:10:7"))); +} + // FIXME: Add support for: // - constructors (copy, move) // - assignment operators (default, copy, move)