[nullability] Emit a warning on array subscripts if the base is nullable.

I noticed that this was missing while preparing to add the corresponding
functionality for smart pointers.

PiperOrigin-RevId: 590861070
Change-Id: I3e16ce8107392c9632e0ab9d43fead1ed32d93d8
diff --git a/nullability/pointer_nullability_diagnosis.cc b/nullability/pointer_nullability_diagnosis.cc
index 85ea4b9..e9c39eb 100644
--- a/nullability/pointer_nullability_diagnosis.cc
+++ b/nullability/pointer_nullability_diagnosis.cc
@@ -87,6 +87,15 @@
       PointerNullabilityDiagnostic::Context::NullableDereference);
 }
 
+SmallVector<PointerNullabilityDiagnostic> diagnoseSubscript(
+    absl::Nonnull<const ArraySubscriptExpr *> Subscript,
+    const MatchFinder::MatchResult &,
+    const TransferStateForDiagnostics<PointerNullabilityLattice> &State) {
+  return diagnoseNonnullExpected(
+      Subscript->getBase(), State.Env,
+      PointerNullabilityDiagnostic::Context::NullableDereference);
+}
+
 SmallVector<PointerNullabilityDiagnostic> diagnoseArrow(
     absl::Nonnull<const MemberExpr *> MemberExpr,
     const MatchFinder::MatchResult &Result,
@@ -329,6 +338,9 @@
                                SmallVector<PointerNullabilityDiagnostic>>()
       // (*)
       .CaseOfCFGStmt<UnaryOperator>(isPointerDereference(), diagnoseDereference)
+      // ([])
+      .CaseOfCFGStmt<ArraySubscriptExpr>(isPointerSubscript(),
+                                         diagnoseSubscript)
       // (->)
       .CaseOfCFGStmt<MemberExpr>(isPointerArrow(), diagnoseArrow)
       // Check compatibility of parameter assignments
diff --git a/nullability/pointer_nullability_matchers.cc b/nullability/pointer_nullability_matchers.cc
index d5e9781..29c858e 100644
--- a/nullability/pointer_nullability_matchers.cc
+++ b/nullability/pointer_nullability_matchers.cc
@@ -15,6 +15,7 @@
 
 using ast_matchers::anyOf;
 using ast_matchers::argumentCountIs;
+using ast_matchers::arraySubscriptExpr;
 using ast_matchers::binaryOperator;
 using ast_matchers::booleanType;
 using ast_matchers::callee;
@@ -36,6 +37,7 @@
 using ast_matchers::hasAnyOperatorName;
 using ast_matchers::hasAnyOverloadedOperatorName;
 using ast_matchers::hasArgument;
+using ast_matchers::hasBase;
 using ast_matchers::hasBody;
 using ast_matchers::hasCanonicalType;
 using ast_matchers::hasCastKind;
@@ -77,6 +79,9 @@
 Matcher<Stmt> isPointerDereference() {
   return unaryOperator(hasOperatorName("*"), hasUnaryOperand(isPointerExpr()));
 }
+Matcher<Stmt> isPointerSubscript() {
+  return arraySubscriptExpr(hasBase(isPointerExpr()));
+}
 Matcher<Stmt> isPointerCheckBinOp() {
   return binaryOperator(hasAnyOperatorName("!=", "=="),
                         hasOperands(isPointerExpr(), isPointerExpr()));
diff --git a/nullability/pointer_nullability_matchers.h b/nullability/pointer_nullability_matchers.h
index 68f2dfe..0ce2059 100644
--- a/nullability/pointer_nullability_matchers.h
+++ b/nullability/pointer_nullability_matchers.h
@@ -37,6 +37,7 @@
 ast_matchers::internal::Matcher<Stmt> isNullPointerLiteral();
 ast_matchers::internal::Matcher<Stmt> isAddrOf();
 ast_matchers::internal::Matcher<Stmt> isPointerDereference();
+ast_matchers::internal::Matcher<Stmt> isPointerSubscript();
 ast_matchers::internal::Matcher<Stmt> isPointerCheckBinOp();
 ast_matchers::internal::Matcher<Stmt> isImplicitCastPointerToBool();
 ast_matchers::internal::Matcher<Stmt> isCallExpr();
diff --git a/nullability/test/basic.cc b/nullability/test/basic.cc
index 1373420..8f6eae6 100644
--- a/nullability/test/basic.cc
+++ b/nullability/test/basic.cc
@@ -233,5 +233,19 @@
   )cc"));
 }
 
+TEST(PointerNullabilityTest, ArraySubscript) {
+  EXPECT_TRUE(checkDiagnostics(R"cc(
+    void target(int *_Nonnull nonnull, int *_Nullable nullable, int *unknown) {
+      nonnull[0];
+      nullable[0];  // [[unsafe]]
+      unknown[0];
+
+      0 [nonnull];
+      0 [nullable];  // [[unsafe]]
+      0 [unknown];
+    }
+  )cc"));
+}
+
 }  // namespace
 }  // namespace clang::tidy::nullability