Check that constructor initializers for pointer fields are null-safe.
Nullable values should not passed to fields marked nonnull.
For example:
```
int * _Nullable makeNullable();
struct Foo {
int * _Nonnull ptr_nonnull;
Foo(): ptr_nonnull(makeNullable); // unsafe
};
```
PiperOrigin-RevId: 475349613
diff --git a/nullability_verification/pointer_nullability_diagnosis.cc b/nullability_verification/pointer_nullability_diagnosis.cc
index 4fedd7a..d1aa1e0 100644
--- a/nullability_verification/pointer_nullability_diagnosis.cc
+++ b/nullability_verification/pointer_nullability_diagnosis.cc
@@ -7,6 +7,7 @@
#include "nullability_verification/pointer_nullability.h"
#include "nullability_verification/pointer_nullability_matchers.h"
#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclCXX.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/Stmt.h"
@@ -127,6 +128,28 @@
: llvm::None;
}
+llvm::Optional<const Stmt*> diagnoseMemberInitializer(
+ const CXXCtorInitializer* CI, const MatchFinder::MatchResult& Result,
+ const Environment& Env) {
+ assert(CI->isAnyMemberInitializer());
+ auto MemberType = CI->getAnyMember()->getType();
+ if (!MemberType->isAnyPointerType()) {
+ return llvm::None;
+ }
+ auto MemberInitExpr = CI->getInit();
+ return isIncompatibleAssignment(MemberType, MemberInitExpr, Env,
+ *Result.Context)
+ // TODO(b/233582219): CtorInitializer is not compatible with the
+ // return type as it is not a Stmt. Therefore, we currently return
+ // the expression in the initializer. The return type should be
+ // modified to work over different AST nodes. For example,
+ // returning a SourceLocation or creating a Diagnostic base class
+ // that will contain more information about the violation and store
+ // the relevant AST nodes.
+ ? llvm::Optional<const Stmt*>(MemberInitExpr)
+ : llvm::None;
+}
+
auto buildDiagnoser() {
return CFGMatchSwitchBuilder<const Environment, llvm::Optional<const Stmt*>>()
// (*)
@@ -137,6 +160,8 @@
.CaseOfCFGStmt<CallExpr>(isCallExpr(), diagnoseCallExpr)
.CaseOfCFGStmt<ReturnStmt>(isPointerReturn(), diagnoseReturn)
.CaseOfCFGStmt<CXXConstructExpr>(isConstructExpr(), diagnoseConstructExpr)
+ .CaseOfCFGInit<CXXCtorInitializer>(isCtorMemberInitializer(),
+ diagnoseMemberInitializer)
.Build();
}
diff --git a/nullability_verification/pointer_nullability_matchers.cc b/nullability_verification/pointer_nullability_matchers.cc
index b5923a0..545765f 100644
--- a/nullability_verification/pointer_nullability_matchers.cc
+++ b/nullability_verification/pointer_nullability_matchers.cc
@@ -15,6 +15,7 @@
using ast_matchers::binaryOperator;
using ast_matchers::callExpr;
using ast_matchers::cxxConstructExpr;
+using ast_matchers::cxxCtorInitializer;
using ast_matchers::cxxThisExpr;
using ast_matchers::declRefExpr;
using ast_matchers::expr;
@@ -28,6 +29,7 @@
using ast_matchers::implicitCastExpr;
using ast_matchers::isAnyPointer;
using ast_matchers::isArrow;
+using ast_matchers::isMemberInitializer;
using ast_matchers::memberExpr;
using ast_matchers::returnStmt;
using ast_matchers::unaryOperator;
@@ -62,6 +64,9 @@
return returnStmt(hasReturnValue(hasType(isAnyPointer())));
}
Matcher<Stmt> isConstructExpr() { return cxxConstructExpr(); }
+Matcher<CXXCtorInitializer> isCtorMemberInitializer() {
+ return cxxCtorInitializer(isMemberInitializer());
+}
} // namespace nullability
} // namespace tidy
diff --git a/nullability_verification/pointer_nullability_matchers.h b/nullability_verification/pointer_nullability_matchers.h
index 0ab6e53..0dfa3c0 100644
--- a/nullability_verification/pointer_nullability_matchers.h
+++ b/nullability_verification/pointer_nullability_matchers.h
@@ -23,6 +23,7 @@
ast_matchers::internal::Matcher<Stmt> isCallExpr();
ast_matchers::internal::Matcher<Stmt> isPointerReturn();
ast_matchers::internal::Matcher<Stmt> isConstructExpr();
+ast_matchers::internal::Matcher<CXXCtorInitializer> isCtorMemberInitializer();
} // namespace nullability
} // namespace tidy
diff --git a/nullability_verification/pointer_nullability_verification_test.cc b/nullability_verification/pointer_nullability_verification_test.cc
index 4c0c8ad..b5af9d1 100644
--- a/nullability_verification/pointer_nullability_verification_test.cc
+++ b/nullability_verification/pointer_nullability_verification_test.cc
@@ -1792,6 +1792,44 @@
)");
}
+TEST(PointerNullabilityTest, ConstructorMemberInitializer) {
+ checkDiagnostics(R"(
+ int * _Nullable makeNullable();
+ struct target {
+ int * _Nonnull ptr_nonnull;
+ int * _Nullable ptr_nullable;
+ int * ptr_unannotated;
+ target(): ptr_nonnull(makeNullable()), // [[unsafe]]
+ ptr_nullable(makeNullable()),
+ ptr_unannotated(makeNullable()) {}
+ };
+ )");
+
+ checkDiagnostics(R"(
+ int * _Nonnull makeNonnull();
+ struct target {
+ int * _Nonnull ptr_nonnull;
+ int * _Nullable ptr_nullable;
+ int * ptr_unannotated;
+ target(): ptr_nonnull(makeNonnull()),
+ ptr_nullable(makeNonnull()),
+ ptr_unannotated(makeNonnull()) {}
+ };
+ )");
+
+ checkDiagnostics(R"(
+ int *makeUnannotated();
+ struct target {
+ int * _Nonnull ptr_nonnull;
+ int * _Nullable ptr_nullable;
+ int * ptr_unannotated;
+ target(): ptr_nonnull(makeUnannotated()),
+ ptr_nullable(makeUnannotated()),
+ ptr_unannotated(makeUnannotated()) {}
+ };
+ )");
+}
+
} // namespace
} // namespace nullability
} // namespace tidy