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);
}
}