Collect information from calls to constructors.
Also add test for collecting information from constructor bodies, just to make it clear we are covering them.
PiperOrigin-RevId: 578915862
Change-Id: Ic4fd38e5f701e56ff4831991def3ef52cb322535
diff --git a/nullability/inference/collect_evidence.cc b/nullability/inference/collect_evidence.cc
index d3d8ce5..e6e1a42 100644
--- a/nullability/inference/collect_evidence.cc
+++ b/nullability/inference/collect_evidence.cc
@@ -23,6 +23,7 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
+#include "clang/AST/DeclCXX.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/OperationKinds.h"
@@ -216,20 +217,12 @@
}
}
-void collectEvidenceFromCallExpr(
+template <typename CallOrConstructExpr>
+void collectEvidenceFromArgsAndParams(
+ const FunctionDecl &CalleeDecl, const CallOrConstructExpr &Expr,
std::vector<std::pair<PointerTypeNullability, Slot>> &InferableCallerSlots,
- const Formula &InferableSlotsConstraint, const CFGElement &Element,
- const dataflow::Environment &Env,
+ const Formula &InferableSlotsConstraint, const dataflow::Environment &Env,
llvm::function_ref<EvidenceEmitter> Emit) {
- // Is this CFGElement a call to a function?
- auto CFGStmt = Element.getAs<clang::CFGStmt>();
- if (!CFGStmt) return;
- auto *CallExpr = dyn_cast_or_null<clang::CallExpr>(CFGStmt->getStmt());
- if (!CallExpr || !CallExpr->getCalleeDecl()) return;
- auto *CalleeDecl =
- dyn_cast_or_null<clang::FunctionDecl>(CallExpr->getCalleeDecl());
- if (!CalleeDecl || !isInferenceTarget(*CalleeDecl)) return;
-
unsigned ParamI = 0;
unsigned ArgI = 0;
// Member operator calls hold the function object as the first argument,
@@ -237,23 +230,23 @@
// For example: Given struct S { bool operator+(int*); }
// The CXXMethodDecl has one parameter, but a call S{}+p is a
// CXXOperatorCallExpr with two arguments: an S and an int*.
- if (isa<clang::CXXOperatorCallExpr>(CallExpr) &&
+ if (isa<clang::CXXOperatorCallExpr>(Expr) &&
isa<clang::CXXMethodDecl>(CalleeDecl))
++ArgI;
// For each pointer parameter of the callee, ...
- for (; ParamI < CalleeDecl->param_size(); ++ParamI, ++ArgI) {
+ for (; ParamI < CalleeDecl.param_size(); ++ParamI, ++ArgI) {
auto ParamType =
- CalleeDecl->getParamDecl(ParamI)->getType().getNonReferenceType();
+ CalleeDecl.getParamDecl(ParamI)->getType().getNonReferenceType();
if (!isSupportedPointerType(ParamType)) continue;
// the corresponding argument should also be a pointer.
- CHECK(isSupportedPointerType(CallExpr->getArg(ArgI)->getType()));
+ CHECK(isSupportedPointerType(Expr.getArg(ArgI)->getType()));
dataflow::PointerValue *PV =
- getPointerValueFromExpr(CallExpr->getArg(ArgI), Env);
+ getPointerValueFromExpr(Expr.getArg(ArgI), Env);
if (!PV) continue;
- SourceLocation ArgLoc = CallExpr->getArg(ArgI)->getExprLoc();
+ SourceLocation ArgLoc = Expr.getArg(ArgI)->getExprLoc();
// TODO: Include inferred annotations from previous rounds when propagating.
auto ParamNullability = getNullabilityAnnotationsFromType(ParamType);
@@ -280,10 +273,47 @@
default:
ArgEvidenceKind = Evidence::UNKNOWN_ARGUMENT;
}
- Emit(*CalleeDecl, paramSlot(ParamI), ArgEvidenceKind, ArgLoc);
+ Emit(CalleeDecl, paramSlot(ParamI), ArgEvidenceKind, ArgLoc);
}
}
+void collectEvidenceFromCallExpr(
+ std::vector<std::pair<PointerTypeNullability, Slot>> &InferableCallerSlots,
+ const Formula &InferableSlotsConstraint, const CFGElement &Element,
+ const dataflow::Environment &Env,
+ llvm::function_ref<EvidenceEmitter> Emit) {
+ // Is this CFGElement a call to a function?
+ auto CFGStmt = Element.getAs<clang::CFGStmt>();
+ if (!CFGStmt) return;
+ auto *CallExpr = dyn_cast_or_null<clang::CallExpr>(CFGStmt->getStmt());
+ if (!CallExpr || !CallExpr->getCalleeDecl()) return;
+ auto *CalleeDecl =
+ dyn_cast_or_null<clang::FunctionDecl>(CallExpr->getCalleeDecl());
+ if (!CalleeDecl || !isInferenceTarget(*CalleeDecl)) return;
+
+ collectEvidenceFromArgsAndParams(*CalleeDecl, *CallExpr, InferableCallerSlots,
+ InferableSlotsConstraint, Env, Emit);
+}
+
+void collectEvidenceFromConstructExpr(
+ std::vector<std::pair<PointerTypeNullability, Slot>> &InferableSlots,
+ const Formula &InferableSlotsConstraint, const CFGElement &Element,
+ const dataflow::Environment &Env,
+ llvm::function_ref<EvidenceEmitter> Emit) {
+ auto CFGStmt = Element.getAs<clang::CFGStmt>();
+ if (!CFGStmt) return;
+ auto *ConstructExpr =
+ dyn_cast_or_null<clang::CXXConstructExpr>(CFGStmt->getStmt());
+ if (!ConstructExpr || !ConstructExpr->getConstructor()) return;
+ auto *ConstructorDecl = dyn_cast_or_null<clang::CXXConstructorDecl>(
+ ConstructExpr->getConstructor());
+ if (!ConstructorDecl || !isInferenceTarget(*ConstructorDecl)) return;
+
+ collectEvidenceFromArgsAndParams(*ConstructorDecl, *ConstructExpr,
+ InferableSlots, InferableSlotsConstraint,
+ Env, Emit);
+}
+
void collectEvidenceFromReturn(
std::vector<std::pair<PointerTypeNullability, Slot>> &InferableSlots,
const Formula &InferableSlotsConstraint, const CFGElement &Element,
@@ -364,6 +394,8 @@
collectEvidenceFromDereference(InferableSlots, Element, Env, Emit);
collectEvidenceFromCallExpr(InferableSlots, InferableSlotsConstraint, Element,
Env, Emit);
+ collectEvidenceFromConstructExpr(InferableSlots, InferableSlotsConstraint,
+ Element, Env, Emit);
collectEvidenceFromReturn(InferableSlots, InferableSlotsConstraint, Element,
Env, Emit);
collectEvidenceFromAssignment(InferableSlots, Element, Env, Emit);
@@ -401,12 +433,12 @@
std::optional<const Decl *> fingerprintedDecl;
Slot Slot;
if (auto *FD = clang::dyn_cast_or_null<FunctionDecl>(&D)) {
- fingerprintedDecl = (ValueDecl *)FD;
+ fingerprintedDecl = FD;
Slot = SLOT_RETURN_TYPE;
} else if (auto *PD = clang::dyn_cast_or_null<ParmVarDecl>(&D)) {
if (auto *Parent = clang::dyn_cast_or_null<FunctionDecl>(
PD->getParentFunctionOrMethod())) {
- fingerprintedDecl = (ValueDecl *)Parent;
+ fingerprintedDecl = Parent;
Slot = paramSlot(PD->getFunctionScopeIndex());
}
}
diff --git a/nullability/inference/collect_evidence_test.cc b/nullability/inference/collect_evidence_test.cc
index 58ed6ca..87567b3 100644
--- a/nullability/inference/collect_evidence_test.cc
+++ b/nullability/inference/collect_evidence_test.cc
@@ -452,6 +452,22 @@
functionNamed("operator()"))));
}
+TEST(CollectEvidenceFromImplementationTest, ConstructorCall) {
+ static constexpr llvm::StringRef Src = R"cc(
+ class S {
+ public:
+ S(Nonnull<int*> a);
+ };
+ void target(int* p) { S s(p); }
+ )cc";
+ EXPECT_THAT(
+ collectEvidenceFromTargetFunction(Src),
+ UnorderedElementsAre(evidence(paramSlot(0), Evidence::BOUND_TO_NONNULL,
+ functionNamed("target")),
+ evidence(paramSlot(0), Evidence::UNKNOWN_ARGUMENT,
+ functionNamed("S"))));
+}
+
TEST(CollectEvidenceFromImplementationTest, PassedToNonnull) {
static constexpr llvm::StringRef Src = R"cc(
void callee(Nonnull<int*> i);
@@ -709,6 +725,7 @@
auto Lambda = []() {}; // Not analyzed yet.
struct S {
+ S() {}
void member();
};
void S::member() {}
@@ -716,11 +733,11 @@
auto Sites = EvidenceSites::discover(AST.context());
EXPECT_THAT(Sites.Declarations,
ElementsAre(declNamed("foo"), declNamed("bar"), declNamed("bar"),
- declNamed("baz"), declNamed("S::member"),
+ declNamed("baz"), declNamed("S::S"),
+ declNamed("S::member"), declNamed("S::member")));
+ EXPECT_THAT(Sites.Implementations,
+ ElementsAre(declNamed("bar"), declNamed("baz"), declNamed("S::S"),
declNamed("S::member")));
- EXPECT_THAT(
- Sites.Implementations,
- ElementsAre(declNamed("bar"), declNamed("baz"), declNamed("S::member")));
}
TEST(EvidenceSitesTest, Variables) {