[nullability] Create `Value`s for pointer prvalues if the framework doesn't.
This fixes a bunch of false positives in unit tests.
PiperOrigin-RevId: 549603184
Change-Id: I0fb05db361eccc768f0c5455249f601075628199
diff --git a/nullability/pointer_nullability_analysis.cc b/nullability/pointer_nullability_analysis.cc
index cb8258a..6786603 100644
--- a/nullability/pointer_nullability_analysis.cc
+++ b/nullability/pointer_nullability_analysis.cc
@@ -23,6 +23,7 @@
#include "clang/AST/Type.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Analysis/CFG.h"
#include "clang/Analysis/FlowSensitive/Arena.h"
#include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
@@ -729,6 +730,21 @@
transferFlowSensitiveNullCheckImplicitCastPtrToBool)
.Build();
}
+
+// Ensure all prvalue expressions of pointer type have a `PointerValue`
+// associated with them so we can track nullability through them.
+void ensurePointerHasValue(const CFGElement &Elt, Environment &Env) {
+ auto S = Elt.getAs<CFGStmt>();
+ if (!S) return;
+
+ auto *E = dyn_cast<Expr>(S->getStmt());
+ if (E == nullptr || !E->isPRValue() || !E->getType()->isPointerType()) return;
+
+ if (Env.getValueStrict(*E) == nullptr)
+ // `createValue()` always produces a value for pointer types.
+ Env.setValueStrict(*E, *Env.createValue(E->getType()));
+}
+
} // namespace
PointerNullabilityAnalysis::PointerNullabilityAnalysis(ASTContext &Context)
@@ -748,6 +764,8 @@
PointerNullabilityLattice &Lattice,
Environment &Env) {
TransferState<PointerNullabilityLattice> State(Lattice, Env);
+
+ ensurePointerHasValue(Elt, Env);
NonFlowSensitiveTransferer(Elt, getASTContext(), State);
FlowSensitiveTransferer(Elt, getASTContext(), State);
}
diff --git a/nullability/test/casts.cc b/nullability/test/casts.cc
index d70da1d..cea0263 100644
--- a/nullability/test/casts.cc
+++ b/nullability/test/casts.cc
@@ -265,7 +265,7 @@
void target(Derived *_Nullable x, Derived *_Nonnull y) {
*static_cast<Base *>(x); // [[unsafe]]
- *static_cast<Base *>(y); // [[unsafe]] TODO: Fix false positive.
+ *static_cast<Base *>(y);
}
)cc"));
@@ -278,12 +278,11 @@
void target(Struct3Arg<1, int *_Nullable, int *> &p) {
*((const Struct3Arg<1, int *, int *> &)p).arg1; // [[unsafe]]
- *((const Struct3Arg<1, int *, int *> &)p) // [[unsafe]]
- .arg2; // TODO: Fix false positive.
- *(int *)p.arg1; // [[unsafe]]
- *(int *)p.arg2; // [[unsafe]] TODO: Fix false positive.
+ *((const Struct3Arg<1, int *, int *> &)p).arg2;
+ *(int *)p.arg1; // [[unsafe]]
+ *(int *)p.arg2;
*(float *)p.arg1; // [[unsafe]]
- *(char *)p.arg2; // [[unsafe]] TODO: Fix false positive.
+ *(char *)p.arg2;
}
)cc"));
@@ -295,7 +294,7 @@
};
void target(Struct2Arg<const int *, const int *_Nullable> &p) {
- *const_cast<int *>(p.arg0); // [[unsafe]] TODO: Fix false positive.
+ *const_cast<int *>(p.arg0);
*const_cast<int *>(p.arg1); // [[unsafe]]
}
)cc"));
diff --git a/nullability/test/fields.cc b/nullability/test/fields.cc
index 697d8d5..1404582 100644
--- a/nullability/test/fields.cc
+++ b/nullability/test/fields.cc
@@ -80,7 +80,6 @@
)cc"));
}
-// TODO: fix false positives due to unsupported PointerValues in the framework.
TEST(PointerNullabilityTest, ChainedFieldDeref) {
EXPECT_TRUE(checkDiagnostics(R"cc(
struct S {
@@ -89,14 +88,14 @@
S *unknown;
};
void target(S &s) {
- *(*s.nonnull).nonnull; // [[unsafe]] TODO: fix false positive
+ *(*s.nonnull).nonnull;
*(*s.nonnull).nullable; // [[unsafe]]
- *(*s.nonnull).unknown; // [[unsafe]] TODO: fix false positive
+ *(*s.nonnull).unknown;
- s.nonnull->nonnull->nonnull; // [[unsafe]] TODO: fix false positive
- s.nonnull->nonnull->nullable; // [[unsafe]] TODO: fix false positive
+ s.nonnull->nonnull->nonnull;
+ s.nonnull->nonnull->nullable;
s.nonnull->nullable->nonnull; // [[unsafe]]
- s.nonnull->unknown->nonnull; // [[unsafe]] TODO: fix false positive
+ s.nonnull->unknown->nonnull;
*&s;
}
diff --git a/nullability/test/pointer_arithmetic.cc b/nullability/test/pointer_arithmetic.cc
index c6ea24e..a873906 100644
--- a/nullability/test/pointer_arithmetic.cc
+++ b/nullability/test/pointer_arithmetic.cc
@@ -10,7 +10,6 @@
namespace clang::tidy::nullability {
namespace {
-// TODO: fix false positives due to unsupported PointerValues in the framework.
TEST(PointerNullabilityTest, PointerArithmetic) {
EXPECT_TRUE(checkDiagnostics(R"cc(
void target(int *_Nullable p, int *_Nonnull q, int *r) {
@@ -20,17 +19,17 @@
*p--; // [[unsafe]]
*+p; // [[unsafe]]
- *++q; // [[unsafe]] TODO: fix false positive
- *q++; // [[unsafe]] TODO: fix false positive
- *--q; // [[unsafe]] TODO: fix false positive
- *q--; // [[unsafe]] TODO: fix false positive
- *+q; // [[unsafe]] TODO: fix false positive
+ *++q;
+ *q++;
+ *--q;
+ *q--;
+ *+q;
- *++r; // [[unsafe]] TODO: fix false positive
- *r++; // [[unsafe]] TODO: fix false positive
- *--r; // [[unsafe]] TODO: fix false positive
- *r--; // [[unsafe]] TODO: fix false positive
- *+r; // [[unsafe]] TODO: fix false positive
+ *++r;
+ *r++;
+ *--r;
+ *r--;
+ *+r;
}
)cc"));
}
diff --git a/nullability/test/templates.cc b/nullability/test/templates.cc
index f994eb3..980021b 100644
--- a/nullability/test/templates.cc
+++ b/nullability/test/templates.cc
@@ -261,7 +261,7 @@
)cc"));
}
-// TODO: Fix false positives and false negatives.
+// TODO: Fix false negatives.
TEST(PointerNullabilityTest,
ClassTemplateInstantiationWithStructsAsParameters) {
EXPECT_TRUE(checkDiagnostics(R"cc(
@@ -290,9 +290,9 @@
*p.arg0.getNullable(); // [[unsafe]]
*p.arg0.getNonnull();
- *p.getT0().unknown; // [[unsafe]] TODO: fix false positive.
+ *p.getT0().unknown;
*p.getT0().nullable; // [[unsafe]]
- *p.getT0().nonnull; // [[unsafe]] TODO: fix false positive.
+ *p.getT0().nonnull;
*p.getT0().getUnknown();
*p.getT0().getNullable(); // [[unsafe]]
@@ -372,15 +372,15 @@
*p.arg3.getNullableUInt(); // [[unsafe]]
*p.arg3.getNullableBool(); // [[unsafe]]
- *p.getT0().unknownChar; // [[unsafe]] TODO: fix false positive.
- *p.getT1().nullableChar; // [[unsafe]]
- *p.getT2().nonnullChar; // [[unsafe]] TODO: fix false positive.
- *p.getT3().unknownLongLong; // [[unsafe]] TODO: fix false positive.
- *p.getT3().nullableDouble; // [[unsafe]]
- *p.getT3().nonnullFloat; // [[unsafe]] TODO: fix false positive.
- *p.getT3().unknownShort; // [[unsafe]] TODO: fix false positive.
- *p.getT3().nullableUInt; // [[unsafe]]
- *p.getT3().nullableBool; // [[unsafe]]
+ *p.getT0().unknownChar;
+ *p.getT1().nullableChar; // [[unsafe]]
+ *p.getT2().nonnullChar;
+ *p.getT3().unknownLongLong;
+ *p.getT3().nullableDouble; // [[unsafe]]
+ *p.getT3().nonnullFloat;
+ *p.getT3().unknownShort;
+ *p.getT3().nullableUInt; // [[unsafe]]
+ *p.getT3().nullableBool; // [[unsafe]]
*p.getT0().getUnknownChar();
*p.getT1().getNullableChar(); // [[unsafe]]
@@ -460,15 +460,15 @@
*p.arg3.getNonnullConstChar();
*p.arg3.getConstNonnullConstChar();
- *p.getT1().constUnknownChar; // [[unsafe]] TODO: fix false positive.
- *p.getT1().unknownConstChar; // [[unsafe]] TODO: fix false positive.
- *p.getT1().constUnknownConstChar; // [[unsafe]] TODO: fix false positive.
- *p.getT2().constNullableChar; // [[unsafe]]
- *p.getT2().nullableConstChar; // [[unsafe]]
+ *p.getT1().constUnknownChar;
+ *p.getT1().unknownConstChar;
+ *p.getT1().constUnknownConstChar;
+ *p.getT2().constNullableChar; // [[unsafe]]
+ *p.getT2().nullableConstChar; // [[unsafe]]
*p.getT2().constNullableConstChar; // [[unsafe]]
- *p.getT3().constNonnullChar; // [[unsafe]] TODO: fix false positive.
- *p.getT3().nonnullConstChar; // [[unsafe]] TODO: fix false positive.
- *p.getT3().constNonnullConstChar; // [[unsafe]] TODO: fix false positive.
+ *p.getT3().constNonnullChar;
+ *p.getT3().nonnullConstChar;
+ *p.getT3().constNonnullConstChar;
*p.getT1().getConstUnknownChar();
*p.getT1().getUnknownConstChar();
@@ -562,7 +562,6 @@
)cc"));
}
-// TODO: Fix false positives.
TEST(PointerNullabilityTest,
ClassTemplateInstantiationWithTemplateStructsAsParameters) {
// Class template with another class template as parameter
@@ -623,9 +622,9 @@
int, int *_Nullable, int *_Nonnull, int>
p) {
*p.arg0.arg0.arg0.arg0; // [[unsafe]]
- *p.arg0.arg0.arg0.arg1; // [[unsafe]] TODO: fix false positive.
+ *p.arg0.arg0.arg0.arg1;
*p.arg0.arg0.arg0.arg2; // [[unsafe]]
- *p.arg0.arg0.arg0.arg3; // [[unsafe]] TODO: fix false positive.
+ *p.arg0.arg0.arg0.arg3;
*p.arg0.arg0.arg0.arg4; // [[unsafe]]
*p.arg0.arg0.arg4; // [[unsafe]]
*p.arg0.arg2; // [[unsafe]]
@@ -659,9 +658,9 @@
int, int *_Nullable, 2, int *_Nonnull, int>
p) {
*p.arg1.arg1.arg1.arg1; // [[unsafe]]
- *p.arg1.arg1.arg1.arg2; // [[unsafe]] TODO: fix false positive.
+ *p.arg1.arg1.arg1.arg2;
*p.arg1.arg1.arg1.arg3; // [[unsafe]]
- *p.arg1.arg1.arg1.arg5; // [[unsafe]] TODO: fix false positive.
+ *p.arg1.arg1.arg1.arg5;
*p.arg1.arg1.arg1.arg6; // [[unsafe]]
*p.arg1.arg1.arg6; // [[unsafe]]
*p.arg1.arg3; // [[unsafe]]
@@ -1079,13 +1078,7 @@
void target() {
*returnSecond<StructUnknownNullable, int *_Nullable>(); // [[unsafe]]
*returnSecond<int *_Nonnull, StructUnknownNullable *>();
- // TODO: The following line is a false positive. We correctly compute the
- // nullability of the expression, as confirmed by the call to
- // `assert_nullability`. However, the dataflow framework currently does
- // not model pointer values for this expression, which results in a (in
- // this case incorrect) nullptr value.
- *returnSecond<int *_Nonnull, StructUnknownNullable>() // [[unsafe]]
- .var0; // TODO: Fix false positive.
+ *returnSecond<int *_Nonnull, StructUnknownNullable>().var0;
__assert_nullability<NK_unspecified>(
returnSecond<int *_Nonnull, StructUnknownNullable>().var0);
*returnSecond<int *_Nonnull, StructUnknownNullable>().var1; // [[unsafe]]