Infer Nonnull parameters from the passing of an influenced value to a Nonnull parameter.

An influenced value is a value that is made Nonnull by the inferred parameter being marked Nonnull.

PiperOrigin-RevId: 568588041
Change-Id: I14939fc1f7a035721829cfcd4ed1e2b10fe1fa10
diff --git a/nullability/inference/collect_evidence.cc b/nullability/inference/collect_evidence.cc
index 7d5837b..05441ff 100644
--- a/nullability/inference/collect_evidence.cc
+++ b/nullability/inference/collect_evidence.cc
@@ -36,6 +36,7 @@
 #include "clang/Analysis/FlowSensitive/Value.h"
 #include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
 #include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/Specifiers.h"
 #include "clang/Index/USRGeneration.h"
 #include "llvm/ADT/DenseMap.h"
@@ -109,6 +110,33 @@
   return {nullptr, SourceLocation()};
 }
 
+// Records evidence derived from the assumption that Value is nonnull.
+// It may be dereferenced, passed as a nonnull param, etc, per EvidenceKind.
+void collectMustBeNonnullEvidence(
+    const dataflow::PointerValue &Value, const dataflow::Environment &Env,
+    SourceLocation Loc,
+    std::vector<std::pair<PointerTypeNullability, Slot>> &InferrableSlots,
+    Evidence::Kind EvidenceKind, llvm::function_ref<EvidenceEmitter> Emit) {
+  auto &A = Env.getDataflowAnalysisContext().arena();
+  auto &NotIsNull = A.makeNot(getPointerNullState(Value).IsNull);
+
+  // If the flow conditions already imply that Value is not null, then we don't
+  // have any new evidence of a necessary annotation.
+  if (Env.flowConditionImplies(NotIsNull)) return;
+
+  // Otherwise, if an inferrable slot being annotated Nonnull would imply that
+  // Value is not null, then we have evidence suggesting that slot should be
+  // annotated. For now, we simply choose the first such slot, sidestepping
+  // complexities around the possibility of multiple such slots, any one of
+  // which would be sufficient if annotated Nonnull.
+  for (auto &[Nullability, Slot] : InferrableSlots) {
+    auto &SlotNonnullImpliesValueNonnull =
+        A.makeImplies(Nullability.isNonnull(A), NotIsNull);
+    if (Env.flowConditionImplies(SlotNonnullImpliesValueNonnull))
+      Emit(*Env.getCurrentFunc(), Slot, EvidenceKind, Loc);
+  }
+}
+
 void collectEvidenceFromDereference(
     std::vector<std::pair<PointerTypeNullability, Slot>> &InferrableSlots,
     const CFGElement &Element, const dataflow::Environment &Env,
@@ -125,24 +153,8 @@
   dataflow::PointerValue *DereferencedValue =
       getPointerValueFromExpr(Target, Env);
   if (!DereferencedValue) return;
-  auto &A = Env.getDataflowAnalysisContext().arena();
-  auto &NotIsNull = A.makeNot(getPointerNullState(*DereferencedValue).IsNull);
-
-  // If the flow conditions already imply the dereferenced value is not null,
-  // then we don't have any new evidence of a necessary annotation.
-  if (Env.flowConditionImplies(NotIsNull)) return;
-
-  // Otherwise, if an inferrable slot being annotated Nonnull would imply that
-  // the dereferenced value is not null, then we have evidence suggesting that
-  // slot should be annotated. For now, we simply choose the first such slot,
-  // sidestepping complexities around the possibility of multiple such slots,
-  // any one of which would be sufficient if annotated Nonnull.
-  for (auto &[Nullability, Slot] : InferrableSlots) {
-    auto &SlotNonnullImpliesDerefValueNonnull =
-        A.makeImplies(Nullability.isNonnull(A), NotIsNull);
-    if (Env.flowConditionImplies(SlotNonnullImpliesDerefValueNonnull))
-      Emit(*Env.getCurrentFunc(), Slot, Evidence::UNCHECKED_DEREFERENCE, Loc);
-  }
+  collectMustBeNonnullEvidence(*DereferencedValue, Env, Loc, InferrableSlots,
+                               Evidence::UNCHECKED_DEREFERENCE, Emit);
 }
 
 const dataflow::Formula *getInferrableSlotsUnknownConstraint(
@@ -159,6 +171,20 @@
   return InferrableSlotsUnknown;
 }
 
+void collectEvidenceFromParamAnnotation(
+    TypeNullability &ParamNullability, const dataflow::PointerValue &ArgPV,
+    std::vector<std::pair<PointerTypeNullability, Slot>> &InferrableCallerSlots,
+    const dataflow::Environment &Env, SourceLocation ArgLoc,
+    llvm::function_ref<EvidenceEmitter> Emit) {
+  //  TODO: Account for variance and each layer of nullability when we handle
+  //  more than top-level pointers.
+  if (ParamNullability.empty()) return;
+  if (ParamNullability[0].concrete() == NullabilityKind::NonNull) {
+    collectMustBeNonnullEvidence(ArgPV, Env, ArgLoc, InferrableCallerSlots,
+                                 Evidence::PASSED_TO_NONNULL, Emit);
+  }
+}
+
 void collectEvidenceFromCallExpr(
     std::vector<std::pair<PointerTypeNullability, Slot>> &InferrableCallerSlots,
     const CFGElement &Element, const dataflow::Environment &Env,
@@ -185,9 +211,9 @@
 
   // For each pointer parameter of the callee, ...
   for (; ParamI < CalleeDecl->param_size(); ++ParamI, ++ArgI) {
-    if (!isSupportedPointerType(
-            CalleeDecl->getParamDecl(ParamI)->getType().getNonReferenceType()))
-      continue;
+    auto ParamType =
+        CalleeDecl->getParamDecl(ParamI)->getType().getNonReferenceType();
+    if (!isSupportedPointerType(ParamType)) continue;
     // the corresponding argument should also be a pointer.
     CHECK(isSupportedPointerType(CallExpr->getArg(ArgI)->getType()));
 
@@ -195,11 +221,15 @@
         getPointerValueFromExpr(CallExpr->getArg(ArgI), Env);
     if (!PV) continue;
 
-    // TODO: Check if the parameter is annotated. If annotated Nonnull, (instead
-    // of collecting evidence for it?) collect evidence similar to a
-    // dereference, i.e. if the argument is not already proven Nonnull, collect
-    // evidence for a parameter that could be annotated Nonnull as a way to
-    // force the argument to be Nonnull.
+    SourceLocation ArgLoc = CallExpr->getArg(ArgI)->getExprLoc();
+
+    // TODO: Include inferred annotations from previous rounds when propagating.
+    auto ParamNullability = getNullabilityAnnotationsFromType(ParamType);
+
+    // Collect evidence from the binding of the argument to the parameter's
+    // nullability, if known.
+    collectEvidenceFromParamAnnotation(
+        ParamNullability, *PV, InferrableCallerSlots, Env, ArgLoc, Emit);
 
     // Emit evidence of the parameter's nullability. First, calculate that
     // nullability based on InferrableSlots for the caller being assigned to
@@ -219,8 +249,7 @@
       default:
         ArgEvidenceKind = Evidence::UNKNOWN_ARGUMENT;
     }
-    Emit(*CalleeDecl, paramSlot(ParamI), ArgEvidenceKind,
-         CallExpr->getArg(ArgI)->getExprLoc());
+    Emit(*CalleeDecl, paramSlot(ParamI), ArgEvidenceKind, ArgLoc);
   }
 }