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)