Set up tooling for verifying C++ code complies with nullability annotations.

PiperOrigin-RevId: 452285783
diff --git a/nullability_verification/pointer_nullability_analysis.cc b/nullability_verification/pointer_nullability_analysis.cc
new file mode 100644
index 0000000..5cde0c3
--- /dev/null
+++ b/nullability_verification/pointer_nullability_analysis.cc
@@ -0,0 +1,139 @@
+// Part of the Crubit project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include "nullability_verification/pointer_nullability_analysis.h"
+
+#include <iostream>
+#include <string>
+
+#include "common/check.h"
+#include "nullability_verification/pointer_nullability_lattice.h"
+#include "nullability_verification/pointer_nullability_matchers.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
+#include "clang/Analysis/FlowSensitive/MatchSwitch.h"
+#include "clang/Analysis/FlowSensitive/Value.h"
+#include "clang/Basic/LLVM.h"
+
+namespace clang {
+namespace tidy {
+namespace nullability {
+
+using ast_matchers::MatchFinder;
+using dataflow::BoolValue;
+using dataflow::Environment;
+using dataflow::MatchSwitchBuilder;
+using dataflow::PointerValue;
+using dataflow::SkipPast;
+using dataflow::TransferState;
+using dataflow::Value;
+
+namespace {
+
+void initialisePointerNullability(
+    const Expr* Expr, const MatchFinder::MatchResult&,
+    TransferState<PointerNullabilityLattice>& State) {
+  if (auto* PointerVal = cast_or_null<PointerValue>(
+          State.Env.getValue(*Expr, SkipPast::Reference))) {
+    if (!State.Lattice.hasPointerNullability(PointerVal)) {
+      State.Lattice.setPointerNullability(PointerVal,
+                                          &State.Env.makeAtomicBoolValue());
+    }
+  }
+}
+
+void transferDereference(const UnaryOperator* UnaryOp,
+                         const MatchFinder::MatchResult&,
+                         TransferState<PointerNullabilityLattice>& State) {
+  auto* PointerExpr = UnaryOp->getSubExpr();
+  if (auto* PointerVal = cast_or_null<PointerValue>(
+          State.Env.getValue(*PointerExpr, SkipPast::Reference))) {
+    auto PointerNullability = State.Lattice.getPointerNullability(PointerVal);
+    CHECK(PointerNullability != nullptr);
+    if (State.Env.flowConditionImplies(*PointerNullability)) {
+      return;
+    }
+  }
+  State.Lattice.addViolation(PointerExpr);
+}
+
+void transferNullCheckComparison(
+    const Expr* NullCheck, const Expr* PointerExpr,
+    TransferState<PointerNullabilityLattice>& State) {
+  if (auto* PointerVal = cast_or_null<PointerValue>(
+          State.Env.getValue(*PointerExpr, SkipPast::Reference))) {
+    auto* PointerNullability = State.Lattice.getPointerNullability(PointerVal);
+    CHECK(PointerNullability != nullptr);
+
+    // For binary operations, the dataflow framework automatically creates a
+    // corresponding BoolVal
+    auto* ExistingDFVal =
+        cast_or_null<BoolValue>(State.Env.getValue(*NullCheck, SkipPast::None));
+    CHECK(ExistingDFVal != nullptr);
+    State.Env.addToFlowCondition(
+        State.Env.makeIff(*ExistingDFVal, *PointerNullability));
+  }
+}
+
+void transferNullCheckImplicitCastPtrToBool(
+    const Expr* CastExpr, const MatchFinder::MatchResult&,
+    TransferState<PointerNullabilityLattice>& State) {
+  if (auto* PointerVal = cast_or_null<PointerValue>(State.Env.getValue(
+          *CastExpr->IgnoreImplicit(), SkipPast::Reference))) {
+    auto* PointerNullability = State.Lattice.getPointerNullability(PointerVal);
+    CHECK(PointerNullability != nullptr);
+
+    auto& CastExprLoc = State.Env.createStorageLocation(*CastExpr);
+    State.Env.setValue(CastExprLoc, *PointerNullability);
+    State.Env.setStorageLocation(*CastExpr, CastExprLoc);
+  }
+}
+
+auto buildTransferer() {
+  return MatchSwitchBuilder<TransferState<PointerNullabilityLattice>>()
+      // Initialise nullability state of pointers
+      .CaseOf<Expr>(isPointerExpr(), initialisePointerNullability)
+      // Pointer dereference
+      .CaseOf<UnaryOperator>(isPointerDereference(), transferDereference)
+      // Nullability check
+      .CaseOf<BinaryOperator>(
+          isNEQNullBinOp(/*BindID=*/"pointer"),
+          [](const BinaryOperator* binOp,
+             const MatchFinder::MatchResult& result,
+             TransferState<PointerNullabilityLattice>& State) {
+            transferNullCheckComparison(
+                binOp, result.Nodes.getNodeAs<Expr>("pointer"), State);
+          })
+      .CaseOf<Expr>(isImplicitCastPtrToBool(),
+                    transferNullCheckImplicitCastPtrToBool)
+      .Build();
+}
+}  // namespace
+
+PointerNullabilityAnalysis::PointerNullabilityAnalysis(ASTContext& Context)
+    : DataflowAnalysis<PointerNullabilityAnalysis, PointerNullabilityLattice>(
+          Context),
+      Transferer(buildTransferer()) {}
+
+void PointerNullabilityAnalysis::transfer(const Stmt* Stmt,
+                                          PointerNullabilityLattice& Lattice,
+                                          Environment& Env) {
+  TransferState<PointerNullabilityLattice> State(Lattice, Env);
+  Transferer(*Stmt, getASTContext(), State);
+}
+
+bool PointerNullabilityAnalysis::merge(QualType Type, const Value& Val1,
+                                       const Environment& Env1,
+                                       const Value& Val2,
+                                       const Environment& Env2,
+                                       Value& MergedVal,
+                                       Environment& MergedEnv) {
+  return false;
+}
+}  // namespace nullability
+}  // namespace tidy
+}  // namespace clang