[nullability] A call to a non-const operator should also be considered mutating.
The additional test in this patch fails without the fix.
PiperOrigin-RevId: 608879021
Change-Id: I295a03fefbf4c5041e693daa211f524138e33479
diff --git a/nullability/pointer_nullability_analysis.cc b/nullability/pointer_nullability_analysis.cc
index 3172144..1450863 100644
--- a/nullability/pointer_nullability_analysis.cc
+++ b/nullability/pointer_nullability_analysis.cc
@@ -1085,23 +1085,40 @@
}
}
+void handleNonConstMemberCall(absl::Nonnull<const CallExpr *> CE,
+ dataflow::RecordStorageLocation *RecordLoc,
+ const MatchFinder::MatchResult &Result,
+ TransferState<PointerNullabilityLattice> &State) {
+ // When a non-const member function is called, reset all pointer-type fields
+ // of the receiver.
+ if (RecordLoc != nullptr) {
+ for (const auto [Field, FieldLoc] : RecordLoc->children()) {
+ if (!isSupportedRawPointerType(Field->getType())) continue;
+ auto &V = *cast<PointerValue>(State.Env.createValue(Field->getType()));
+ State.Env.setValue(*FieldLoc, V);
+ }
+ State.Lattice.clearConstMethodReturnValues(*RecordLoc);
+ }
+
+ // Perform default handling.
+ transferValue_CallExpr(CE, Result, State);
+}
+
void transferValue_NonConstMemberCall(
absl::Nonnull<const CXXMemberCallExpr *> MCE,
const MatchFinder::MatchResult &Result,
TransferState<PointerNullabilityLattice> &State) {
- // When a non-const member function is called, reset all pointer-type fields
- // of the implicit object.
- if (dataflow::RecordStorageLocation *RecordLoc =
- dataflow::getImplicitObjectLocation(*MCE, State.Env)) {
- for (const auto [Field, FieldLoc] : RecordLoc->children()) {
- if (!isSupportedRawPointerType(Field->getType())) continue;
- Value *V = State.Env.createValue(Field->getType());
- State.Env.setValue(*FieldLoc, *V);
- }
- State.Lattice.clearConstMethodReturnValues(*RecordLoc);
- }
- // The nullability of the Expr itself still needs to be handled.
- transferValue_CallExpr(MCE, Result, State);
+ handleNonConstMemberCall(
+ MCE, dataflow::getImplicitObjectLocation(*MCE, State.Env), Result, State);
+}
+
+void transferValue_NonConstMemberOperatorCall(
+ absl::Nonnull<const CXXOperatorCallExpr *> OCE,
+ const MatchFinder::MatchResult &Result,
+ TransferState<PointerNullabilityLattice> &State) {
+ auto *RecordLoc = cast_or_null<dataflow::RecordStorageLocation>(
+ State.Env.getStorageLocation(*OCE->getArg(0)));
+ handleNonConstMemberCall(OCE, RecordLoc, Result, State);
}
void transferType_DeclRefExpr(absl::Nonnull<const DeclRefExpr *> DRE,
@@ -1469,6 +1486,9 @@
transferValue_ConstMemberCall)
.CaseOfCFGStmt<CXXMemberCallExpr>(isNonConstMemberCall(),
transferValue_NonConstMemberCall)
+ .CaseOfCFGStmt<CXXOperatorCallExpr>(
+ isNonConstMemberOperatorCall(),
+ transferValue_NonConstMemberOperatorCall)
.CaseOfCFGStmt<CallExpr>(ast_matchers::callExpr(), transferValue_CallExpr)
.CaseOfCFGStmt<Expr>(isSmartPointerGlValue(), transferValue_SmartPointer)
.CaseOfCFGStmt<MemberExpr>(isSmartPointerArrowMemberExpr(),
diff --git a/nullability/pointer_nullability_matchers.cc b/nullability/pointer_nullability_matchers.cc
index 47d26ce..854b68e 100644
--- a/nullability/pointer_nullability_matchers.cc
+++ b/nullability/pointer_nullability_matchers.cc
@@ -113,6 +113,10 @@
return cxxMemberCallExpr(callee(cxxMethodDecl(unless(isConst()))));
}
+Matcher<Stmt> isNonConstMemberOperatorCall() {
+ return cxxOperatorCallExpr(callee(cxxMethodDecl(unless(isConst()))));
+}
+
Matcher<Stmt> isSmartPointerGlValue() {
return expr(hasType(isSupportedSmartPointer()), isGLValue());
}
diff --git a/nullability/pointer_nullability_matchers.h b/nullability/pointer_nullability_matchers.h
index 727f428..acd8b9f 100644
--- a/nullability/pointer_nullability_matchers.h
+++ b/nullability/pointer_nullability_matchers.h
@@ -47,6 +47,7 @@
ast_matchers::internal::Matcher<CXXCtorInitializer> isCtorMemberInitializer();
ast_matchers::internal::Matcher<Stmt> isZeroParamConstMemberCall();
ast_matchers::internal::Matcher<Stmt> isNonConstMemberCall();
+ast_matchers::internal::Matcher<Stmt> isNonConstMemberOperatorCall();
ast_matchers::internal::Matcher<Stmt> isSmartPointerGlValue();
ast_matchers::internal::Matcher<Stmt> isSmartPointerArrowMemberExpr();
ast_matchers::internal::Matcher<Stmt> isSmartPointerConstructor();
diff --git a/nullability/test/function_calls.cc b/nullability/test/function_calls.cc
index 1c81318..4c1d926 100644
--- a/nullability/test/function_calls.cc
+++ b/nullability/test/function_calls.cc
@@ -774,6 +774,7 @@
struct C {
int *_Nullable property() const;
void may_mutate();
+ C &operator=(const C &);
};
void target() {
C obj;
@@ -781,6 +782,11 @@
obj.may_mutate();
*obj.property(); // [[unsafe]]
};
+ if (obj.property() != nullptr) {
+ // A non-const operator call may mutate as well.
+ obj = C();
+ *obj.property(); // [[unsafe]]
+ };
if (obj.property() != nullptr) *obj.property();
}
)cc"));