Implement transfer function for checking that member accesses on pointers via the arrow (->) operator are null-safe.

We use a single matcher to handle both cases:\
Case 1: checking -> access on pointers\
Case 2: initialisation of null state for pointers which are members

We don't use distinct matchers as the `MatchSwitch` utility will only invoke one matcher case at a time, so the situation where both cases need to be invoked (see example below) will not be handled correctly.

```
struct Foo {
  Foo* MemberPtr;
};
Foo* BasePtr;
BasePtr->MemberPtr; // Matches case 1 and 2
```

PiperOrigin-RevId: 454133816
diff --git a/nullability_verification/pointer_nullability_analysis.cc b/nullability_verification/pointer_nullability_analysis.cc
index 99ffa5e..a512ec4 100644
--- a/nullability_verification/pointer_nullability_analysis.cc
+++ b/nullability_verification/pointer_nullability_analysis.cc
@@ -44,10 +44,9 @@
 }
 
 void initialisePointerNotNullProperty(
-    const Expr* Expr, const MatchFinder::MatchResult&,
-    TransferState<PointerNullabilityLattice>& State) {
+    const Expr* PointerExpr, TransferState<PointerNullabilityLattice>& State) {
   if (auto* PointerVal = cast_or_null<PointerValue>(
-          State.Env.getValue(*Expr, SkipPast::Reference))) {
+          State.Env.getValue(*PointerExpr, SkipPast::Reference))) {
     if (!State.Lattice.hasPointerNotNullProperty(PointerVal)) {
       State.Lattice.setPointerNotNullProperty(PointerVal,
                                               &State.Env.makeAtomicBoolValue());
@@ -55,6 +54,12 @@
   }
 }
 
+void transferInitPointerVariableReference(
+    const Expr* Expr, const MatchFinder::MatchResult&,
+    TransferState<PointerNullabilityLattice>& State) {
+  initialisePointerNotNullProperty(Expr, State);
+}
+
 void transferNullPointerLiteral(
     const Expr* NullPointer, const MatchFinder::MatchResult& Result,
     TransferState<PointerNullabilityLattice>& State) {
@@ -84,16 +89,33 @@
                                           &State.Env.getBoolLiteralValue(true));
 }
 
-void transferDereference(const UnaryOperator* UnaryOp,
-                         const MatchFinder::MatchResult&,
-                         TransferState<PointerNullabilityLattice>& State) {
-  auto* PointerExpr = UnaryOp->getSubExpr();
+void transferPointerAccess(const Expr* PointerExpr,
+                           TransferState<PointerNullabilityLattice>& State) {
   auto& PointerNotNull = getPointerNotNullProperty(PointerExpr, State);
   if (!State.Env.flowConditionImplies(PointerNotNull)) {
     State.Lattice.addViolation(PointerExpr);
   }
 }
 
+void transferDereference(const UnaryOperator* UnaryOp,
+                         const MatchFinder::MatchResult&,
+                         TransferState<PointerNullabilityLattice>& State) {
+  transferPointerAccess(UnaryOp->getSubExpr(), State);
+}
+
+void transferMemberExprInvolvingPointers(
+    const MemberExpr* MemberExpr, const MatchFinder::MatchResult&,
+    TransferState<PointerNullabilityLattice>& State) {
+  if (MemberExpr->isArrow()) {
+    // Base expr is a pointer, check that (->) access is safe
+    transferPointerAccess(MemberExpr->getBase(), State);
+  }
+  if (MemberExpr->getType()->isAnyPointerType()) {
+    // Accessed member is a pointer, initialise its nullability
+    initialisePointerNotNullProperty(MemberExpr, State);
+  }
+}
+
 void transferNullCheckComparison(
     const BinaryOperator* BinaryOp, const MatchFinder::MatchResult& result,
     TransferState<PointerNullabilityLattice>& State) {
@@ -144,8 +166,11 @@
   return MatchSwitchBuilder<TransferState<PointerNullabilityLattice>>()
       // Handles initialization of the null states of pointers
       .CaseOf<Expr>(isPointerVariableReference(),
-                    initialisePointerNotNullProperty)
-      .CaseOf<Expr>(isPointerMemberExpr(), initialisePointerNotNullProperty)
+                    transferInitPointerVariableReference)
+      // Handles initialization of null states of member pointers and safety of
+      // member access (->) on pointers
+      .CaseOf<MemberExpr>(isMemberExprInvolvingPointers(),
+                          transferMemberExprInvolvingPointers)
       // Handles nullptr
       .CaseOf<Expr>(isNullPointerLiteral(), transferNullPointerLiteral)
       // Handles address of operator (&var)