blob: ad16d98306e9ca7c152bbbc44cdce7daf84939dd [file] [log] [blame]
// 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
#ifndef CRUBIT_NULLABILITY_POINTER_NULLABILITY_H_
#define CRUBIT_NULLABILITY_POINTER_NULLABILITY_H_
// This file extends the dataflow framework's Value model to track nullability.
// We attach two boolean properties to each modeled pointer value:
// - is_null: whether the pointer may actually be null
// If this is false, dereferencing is safe.
// - from_nullable: whether the originating expression was considered nullable
// (e.g. a nullptr literal, or a reference to a Nullable-annotated variable)
// If this is false, dereferencing may be safe: we don't know the contract.
#include <optional>
#include "absl/base/nullability.h"
#include "nullability/type_nullability.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTDumper.h"
#include "clang/AST/Expr.h"
#include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
#include "clang/Analysis/FlowSensitive/Formula.h"
#include "clang/Analysis/FlowSensitive/Value.h"
#include "clang/Basic/Specifiers.h"
namespace clang::tidy::nullability {
/// Name of the synthetic field that models a smart pointer's underlying
/// pointer.
/// Where possible, use accessors such as `getPointerValueFromSmartPointer()`
/// instead of accessing this field directly.
inline constexpr llvm::StringRef PtrField = "ptr";
/// Returns the `PointerValue` allocated to `PointerExpr` if available.
/// Otherwise, returns nullptr.
absl::Nullable<dataflow::PointerValue *> getRawPointerValue(
absl::Nonnull<const Expr *> PointerExpr, const dataflow::Environment &Env);
/// Returns the `PointerValue` underlying a smart pointer, or null if no
/// `PointerValue` is assigned to the smart pointer in the environment.
/// If `SmartPointerLoc` is null, returns null.
absl::Nullable<dataflow::PointerValue *> getPointerValueFromSmartPointer(
absl::Nullable<dataflow::RecordStorageLocation *> SmartPointerLoc,
const dataflow::Environment &Env);
/// Returns the `PointerValue` underlying a smart pointer expression, if
/// available.
/// Returns null if the expression is not associated with a storage location or
/// the smart pointer is not associated with a `PointerValue`.
absl::Nullable<dataflow::PointerValue *> getSmartPointerValue(
absl::Nonnull<const Expr *> SmartPointerExpr,
const dataflow::Environment &Env);
/// Returns the `PointerValue` for a raw or smart pointer expression, if
/// available.
/// Use this function only if the expression can actually be either a raw or
/// smart pointer; otherwise, use `getRawPointerValue()` or
/// `getSmartPointerValue()`.
absl::Nullable<dataflow::PointerValue *> getPointerValue(
absl::Nonnull<const Expr *> PointerExpr, const dataflow::Environment &Env);
/// Sets the `PointerValue` underlying a smart pointer. If `PointerValue` is
/// null, clears any association between the smart pointer and an underlying
/// `PointerValue` in the environment.
void setSmartPointerValue(dataflow::RecordStorageLocation &SmartPointerLoc,
absl::Nullable<dataflow::PointerValue *> Val,
dataflow::Environment &Env);
// Sets the `PointerValue` underlying a smart pointer to null.
void setSmartPointerToNull(dataflow::RecordStorageLocation &SmartPointerLoc,
dataflow::Environment &Env);
// Returns true if the pointer has all properties necessary for representing
// complete nullness information.
// Otherwise, returns false.
//
// Pointers that are the value of some expression always have null state once
// that expression has been analyzed. Other pointers, like the values of unused
// parameters, may lack this state. This state is only set by
// PointerNullabilityAnalysis, not by the dataflow framework.
bool hasPointerNullState(const dataflow::PointerValue &PointerVal);
/// The properties representing nullness information for a pointer.
///
/// We attach these properties to every PointerValue taken by an expression.
///
/// A null pointer for `FromNullable` or `IsNull` represents "top", i.e. we have
/// no information on this property.
struct PointerNullState {
/// Did the pointer come from a known-nullable source?
absl::Nullable<const dataflow::Formula *> FromNullable;
/// Is the pointer's value null?
absl::Nullable<const dataflow::Formula *> IsNull;
// These are independent: sources with unknown nullability can yield nullptr!
};
/// Returns the properties representing the nullness information of a pointer.
PointerNullState getPointerNullState(const dataflow::PointerValue &PointerVal);
/// Creates the nullness properties on `PointerVal` if not already initialised.
///
/// We call this when the framework produces a PointerValue for an expression.
/// This ensures that the variable has usable "from nullable" and "is null"
/// boolean variables, and that they are constrained based on the *original*
/// source of the PointerValue.
///
/// For example:
/// Unknown<int> *x = makeNullable();
/// ~~~~~~~~~~~~~~ <-- initPointerNullState(Nullable)
/// *x;
/// ~ <-- initPointerNullState(Unknown) - no effect, already initialized
///
/// The constraints are added to the context as a non-flow-sensitive invariant,
/// so the source nullability may not depend on flow-sensitive information.
///
/// (We assume that the framework will not provide the same pointer from
/// different initial sources, so the `Source` nullability is the same
/// regardless of block evaluation order).
void initPointerNullState(
dataflow::PointerValue &PointerVal, dataflow::DataflowAnalysisContext &Ctx,
std::optional<PointerTypeNullability> Source = std::nullopt);
/// Initializes the nullness properties on `PointerVal` from `State`.
///
/// This overload may only be called on a freshly created `PointerValue` that
/// does not yet have nullability properties.
void initPointerNullState(dataflow::PointerValue &PointerVal,
dataflow::DataflowAnalysisContext &Ctx,
PointerNullState State);
/// Variant of initPointerNullState, where the pointer is guaranteed null.
/// (This is flow-insensitive, but PointerTypeNullability can't represent it).
void initNullPointer(dataflow::PointerValue &PointerVal,
dataflow::DataflowAnalysisContext &Ctx);
/// Creates a null pointer with the given pointee type. The null state for the
/// pointer is set to reflect that the pointer is null. This always returns the
/// same `PointerValue` for a given `PointeeType`.
dataflow::PointerValue &createNullPointer(QualType PointeeType,
dataflow::Environment &Env);
/// Returns true if `PointerVal` is known to be null or is from a nullable
/// source and may be null.
bool isNullable(
const dataflow::PointerValue &PointerVal, const dataflow::Environment &Env,
absl::Nullable<const dataflow::Formula *> AdditionalConstraints = nullptr);
/// Returns the strongest provable assertion we can make about `PointerVal`.
/// If PointerVal may not be null, returns Nonnull.
/// If PointerVal is known to be null or is from a nullable source and may be
/// null, returns Nullable.
/// Otherwise, returns Unspecified.
clang::NullabilityKind getNullability(
const dataflow::PointerValue &PointerVal, const dataflow::Environment &Env,
absl::Nullable<const dataflow::Formula *> AdditionalConstraints = nullptr);
/// Returns the strongest provable assertion we can make about the value of
/// `E` in `Env`.
clang::NullabilityKind getNullability(
absl::Nonnull<const Expr *> E, const dataflow::Environment &Env,
absl::Nullable<const dataflow::Formula *> AdditionalConstraints = nullptr);
// Work around the lack of Expr.dump() etc with an ostream but no ASTContext.
template <typename T>
void dump(const T &Node, llvm::raw_ostream &OS) {
clang::ASTDumper(OS, /*ShowColors=*/false).Visit(Node);
}
} // namespace clang::tidy::nullability
#endif // CRUBIT_NULLABILITY_POINTER_NULLABILITY_H_