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