Add transfer function for `MaterializeTemporaryExpr` in nullability verification This allows us to handle expressions where `prvalue` temporaries are written to memory so they can be bound to references. For example, passing a member call to a function that takes a const reference argument. PiperOrigin-RevId: 502610175
diff --git a/nullability_verification/pointer_nullability_analysis.cc b/nullability_verification/pointer_nullability_analysis.cc index fd73e18..a750066 100644 --- a/nullability_verification/pointer_nullability_analysis.cc +++ b/nullability_verification/pointer_nullability_analysis.cc
@@ -486,6 +486,26 @@ }); } +void transferNonFlowSensitiveMaterializeTemporaryExpr( + const MaterializeTemporaryExpr* MTE, const MatchFinder::MatchResult& MR, + TransferState<PointerNullabilityLattice>& State) { + State.Lattice.insertExprNullabilityIfAbsent(MTE, [&]() { + auto BaseNullability = State.Lattice.getExprNullability(MTE->getSubExpr()); + if (BaseNullability.has_value()) { + return BaseNullability->vec(); + } else { + // Since we process child nodes before parents, we should already have + // computed the base (child) nullability. However, this is not true in all + // test cases. So, we return unspecified nullability annotations. + // TODO: Fix this issue, add a CHECK(BaseNullability.has_value()) and + // remove the else branch. + llvm::dbgs() << "Nullability of child node not found\n"; + return std::vector<NullabilityKind>(countPointersInType(MTE->getType()), + NullabilityKind::Unspecified); + } + }); +} + auto buildNonFlowSensitiveTransferer() { return CFGMatchSwitchBuilder<TransferState<PointerNullabilityLattice>>() .CaseOfCFGStmt<DeclRefExpr>(ast_matchers::declRefExpr(), @@ -496,6 +516,9 @@ transferNonFlowSensitiveMemberCallExpr) .CaseOfCFGStmt<CastExpr>(ast_matchers::castExpr(), transferNonFlowSensitiveCastExpr) + .CaseOfCFGStmt<MaterializeTemporaryExpr>( + ast_matchers::materializeTemporaryExpr(), + transferNonFlowSensitiveMaterializeTemporaryExpr) .Build(); }
diff --git a/nullability_verification/pointer_nullability_verification_test.cc b/nullability_verification/pointer_nullability_verification_test.cc index 92815d9..0dd5942 100644 --- a/nullability_verification/pointer_nullability_verification_test.cc +++ b/nullability_verification/pointer_nullability_verification_test.cc
@@ -3022,12 +3022,6 @@ } )cc")); - // Member variables and member calls on a struct with nested template - // arguments. - // TODO: Add non-flow-sensitive transfer function for the dereference - // operator. - // TODO: Handle member call arguments (includes writing - // transferMaterializeTemporaryExpr). EXPECT_TRUE(checkDiagnostics(Declarations + R"cc( template <typename T0, typename T1> struct Struct2Arg { @@ -3063,26 +3057,22 @@ <NK_unspecified, NK_nullable, NK_nonnull, NK_nullable, NK_nullable>( p.arg1); - // TODO: Add support for member call arguments. - // __assert_nullability<NK_unspecified, NK_nullable>(p->getT0()); - // __assert_nullability // TODO: fix false negative. - // <NK_unspecified, NK_nullable, NK_unspecified>(p->getT0()); - // __assert_nullability // TODO: fix false negative. - // <NK_unspecified>(p->getT0()); - // __assert_nullability // TODO: fix false negative. - // <NK_unspecified, NK_nullable, NK_nullable>(p->arg0); - // __assert_nullability // TODO: fix false negative. - // <NK_nullable>(p->arg0); + __assert_nullability<NK_unspecified, NK_nullable>(p.getT0()); + __assert_nullability<NK_nonnull>(p.getT1().getT0().getT1()); - // __assert_nullability<NK_nonnull>(p->getT1().getT0().getT1()); - // __assert_nullability<NK_nonnull>(p->getT1().arg0.getT1()); - // __assert_nullability<NK_nonnull>(p->arg1.getT0().arg1); - // __assert_nullability<NK_nonnull>(p->arg1.arg0.arg1); + __assert_nullability // [[unsafe]] + <NK_unspecified, NK_nullable, NK_unspecified>(p.getT0()); + __assert_nullability // [[unsafe]] + <NK_unspecified>(p.getT0()); - // __assert_nullability // TODO: fix false negative. - // <>(p->getT1().getT0().getT1()); - // __assert_nullability // TODO: fix false negative. - // <NK_nonnull, NK_nonnull>(p->arg1.getT0().arg1); + __assert_nullability<NK_nonnull>(p.getT1().arg0.getT1()); + __assert_nullability<NK_nonnull>(p.arg1.getT0().arg1); + __assert_nullability<NK_nonnull>(p.arg1.arg0.arg1); + + __assert_nullability // [[unsafe]] + <>(p.getT1().getT0().getT1()); + __assert_nullability // [[unsafe]] + <NK_nonnull, NK_nonnull>(p.arg1.getT0().arg1); } )cc")); } @@ -3156,6 +3146,71 @@ )cc")); } +// TODO: Handle non-flow-sensitive nullability of free functions to make the +// following test work: +TEST(PointerNullabilityTest, NonFlowSensitiveMaterializeTemporaryExpr) { + EXPECT_TRUE(checkDiagnostics(R"cc( + int *_Nonnull makeNonnull(); + int *_Nullable makeNullable(); + int *makeUnannotated(); + + template <typename T> + T identity(const T &); + + void target() { + { + *identity<int *_Nonnull>(makeNonnull()); + int *const &p = makeNonnull(); + *p; + } + { + *identity<int *_Nullable>(makeNullable()); // TODO: Fix false negative. + int *const &p = makeNullable(); + *p; // [[unsafe]] + } + { + *identity<int *>(makeUnannotated()); + int *const &p = makeUnannotated(); + *p; + } + } + )cc")); + + EXPECT_TRUE(checkDiagnostics(R"cc( + template <typename T0, typename T1> + struct Struct2Arg { + T0 getT0(); + T1 getT1(); + }; + + template <typename T> + T make(); + + template <typename T> + T identity(const T &); + + void target(Struct2Arg<int *, int *_Nullable> &p) { + *identity<Struct2Arg<int *, int *_Nullable>>(p).getT0(); + *identity<Struct2Arg<int *, int *_Nullable>>( + make<Struct2Arg<int *, int *_Nullable>>()) + .getT0(); + *identity<Struct2Arg<int *, int *_Nullable>>( + Struct2Arg<int *, int *_Nullable>(p)) + .getT0(); + *identity<int *>(p.getT0()); + *identity<Struct2Arg<int *, int *_Nullable>>(p).getT1(); // TODO: Fix + // false negative. + *identity<Struct2Arg<int *, int *_Nullable>>( + make<Struct2Arg<int *, int *_Nullable>>()) + .getT1(); + *identity<Struct2Arg<int *, int *_Nullable>>( + Struct2Arg<int *, int *_Nullable>(p)) + .getT1(); // TODO: Fix false negative. + *identity<int *_Nullable>(p.getT1()); // TODO: Fix false negative. + } + )cc")); +} + } // namespace } // namespace nullability } // namespace tidy