Refactoring common null-safety checking functions.

- `getNullabilityKind` returns the nullability kind specified by the nullability annotation on the type if present, otherwise returns the default NullabilityKind::Unspecified.
- `isUnhandledOrNullable` checks that an expression is unhandled by the analysis or is known to be nullable.
- `isIncompatibleAssignment` checks that the nullability of the actual expression complies with nullability annotations on the declared type at the point of use.

PiperOrigin-RevId: 468783498
diff --git a/nullability_verification/BUILD b/nullability_verification/BUILD
index 9ff987c..3ebbdbd 100644
--- a/nullability_verification/BUILD
+++ b/nullability_verification/BUILD
@@ -48,6 +48,7 @@
     deps = [
         "@llvm-project//clang:analysis",
         "@llvm-project//clang:ast",
+        "@llvm-project//clang:basic",
         "@llvm-project//llvm:Support",
     ],
 )
diff --git a/nullability_verification/pointer_nullability.cc b/nullability_verification/pointer_nullability.cc
index c53dfd9..04ba12b 100644
--- a/nullability_verification/pointer_nullability.cc
+++ b/nullability_verification/pointer_nullability.cc
@@ -23,6 +23,10 @@
 constexpr llvm::StringLiteral kKnown = "is_known";
 constexpr llvm::StringLiteral kNotNull = "is_notnull";
 
+NullabilityKind getNullabilityKind(QualType Type, ASTContext& Ctx) {
+  return Type->getNullability(Ctx).value_or(NullabilityKind::Unspecified);
+}
+
 PointerValue* getPointerValueFromExpr(const Expr* PointerExpr,
                                       const Environment& Env) {
   return cast_or_null<PointerValue>(
diff --git a/nullability_verification/pointer_nullability.h b/nullability_verification/pointer_nullability.h
index 01c9c75..1f8fc70 100644
--- a/nullability_verification/pointer_nullability.h
+++ b/nullability_verification/pointer_nullability.h
@@ -7,16 +7,22 @@
 
 #include <utility>
 
+#include "clang/AST/ASTContext.h"
 #include "clang/AST/Expr.h"
 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
 #include "clang/Analysis/FlowSensitive/Value.h"
+#include "clang/Basic/Specifiers.h"
 
 namespace clang {
 namespace tidy {
 namespace nullability {
 
-/// Returns the `PointerValue` allocated to `PointerExpr` if available,
-/// otherwise returns nullptr.
+/// Returns the `NullabilityKind` corresponding to the nullability annotation on
+/// `Type` if present. Otherwise, returns `NullabilityKind::Unspecified`.
+NullabilityKind getNullabilityKind(QualType Type, ASTContext& Ctx);
+
+/// Returns the `PointerValue` allocated to `PointerExpr` if available.
+/// Otherwise, returns nullptr.
 dataflow::PointerValue* getPointerValueFromExpr(
     const Expr* PointerExpr, const dataflow::Environment& Env);
 
diff --git a/nullability_verification/pointer_nullability_diagnosis.cc b/nullability_verification/pointer_nullability_diagnosis.cc
index 7387eef..36051f6 100644
--- a/nullability_verification/pointer_nullability_diagnosis.cc
+++ b/nullability_verification/pointer_nullability_diagnosis.cc
@@ -6,8 +6,11 @@
 
 #include "nullability_verification/pointer_nullability.h"
 #include "nullability_verification/pointer_nullability_matchers.h"
+#include "clang/AST/ASTContext.h"
 #include "clang/AST/Expr.h"
+#include "clang/AST/Stmt.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
 #include "clang/Basic/Specifiers.h"
 
 namespace clang {
@@ -19,26 +22,37 @@
 
 namespace {
 
-llvm::Optional<const Stmt*> diagnosePointerAccess(const Stmt* PointerAccessExpr,
-                                                  const Expr* PointerExpr,
-                                                  const Environment& Env) {
-  auto* PointerVal = getPointerValueFromExpr(PointerExpr, Env);
-  if (!PointerVal || isNullable(*PointerVal, Env)) {
-    return PointerAccessExpr;
-  }
-  return llvm::None;
+// Returns true if `Expr` is uninterpreted or known to be nullable.
+bool isNullableOrUntracked(const Expr* E, const Environment& Env) {
+  auto* ActualVal = getPointerValueFromExpr(E, Env);
+  return !ActualVal || isNullable(*ActualVal, Env);
+}
+
+// Returns true if an uninterpreted or nullable `Expr` was assigned to a
+// construct with a non-null `DeclaredType`.
+bool isIncompatibleAssignment(QualType DeclaredType, const Expr* E,
+                              const Environment& Env, ASTContext& Ctx) {
+  assert(DeclaredType->isAnyPointerType());
+  return getNullabilityKind(DeclaredType, Ctx) == NullabilityKind::NonNull &&
+         isNullableOrUntracked(E, Env);
 }
 
 llvm::Optional<const Stmt*> diagnoseDereference(const UnaryOperator* UnaryOp,
                                                 const MatchFinder::MatchResult&,
                                                 const Environment& Env) {
-  return diagnosePointerAccess(UnaryOp, UnaryOp->getSubExpr(), Env);
+  if (isNullableOrUntracked(UnaryOp->getSubExpr(), Env)) {
+    return UnaryOp;
+  }
+  return llvm::None;
 }
 
 llvm::Optional<const Stmt*> diagnoseArrow(
     const MemberExpr* MemberExpr, const MatchFinder::MatchResult& Result,
     const Environment& Env) {
-  return diagnosePointerAccess(MemberExpr, MemberExpr->getBase(), Env);
+  if (isNullableOrUntracked(MemberExpr->getBase(), Env)) {
+    return MemberExpr;
+  }
+  return llvm::None;
 }
 
 // TODO(b/233582219): Handle call expressions whose callee is not a decl (e.g.
@@ -57,15 +71,11 @@
 
   for (unsigned int I = 0; I < ParamTypes.size(); ++I) {
     auto ParamType = ParamTypes[I];
-    if (!ParamType->isAnyPointerType() ||
-        ParamType->getNullability(*Result.Context)
-                .value_or(NullabilityKind::Unspecified) !=
-            NullabilityKind::NonNull) {
+    if (!ParamType->isAnyPointerType()) {
       continue;
     }
-    auto* Arg = CE->getArg(I);
-    auto* PointerVal = getPointerValueFromExpr(Arg, Env);
-    if (!PointerVal || isNullable(*PointerVal, Env)) {
+    if (isIncompatibleAssignment(ParamType, CE->getArg(I), Env,
+                                 *Result.Context)) {
       return llvm::Optional<const Stmt*>(CE);
     }
   }