blob: 33ae562f21e39f3b93b8e3f876b7415b4dd3dcd6 [file]
// 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_LATTICE_H_
#define CRUBIT_NULLABILITY_POINTER_NULLABILITY_LATTICE_H_
#include <functional>
#include <optional>
#include <ostream>
#include "absl/base/nullability.h"
#include "nullability/type_nullability.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/Analysis/FlowSensitive/ASTOps.h"
#include "clang/Analysis/FlowSensitive/CachedConstAccessorsLattice.h"
#include "clang/Analysis/FlowSensitive/DataflowLattice.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
namespace clang::tidy::nullability {
class PointerNullabilityLatticeBase {
public:
struct NonFlowSensitiveState {
// Nullability interpretation of types as set e.g. by per-file #pragmas.
TypeNullabilityDefaults Defaults;
llvm::DenseMap<const Expr*, TypeNullability> ExprToNullability;
// Overridden symbolic nullability for pointer-typed decls.
// These are set by PointerNullabilityAnalysis::assignNullabilityVariable,
// and take precedence over the declared type.
llvm::DenseMap<const ValueDecl* absl_nonnull, PointerTypeNullability>
DeclTopLevelNullability;
// Nonnull pointer fields (raw and smart) of the class whose destructor is
// being analyzed that must be modeled as nullable at destructor entry,
// because a move operation or `&&`-qualified method may have nulled them
// out before destruction. Empty unless analyzing such a destructor.
llvm::DenseSet<const FieldDecl*> FieldsToTreatAsNullableAtDestructorEntry;
// Flow-sensitive nullability of by-value captured pointers, recorded at the
// capture site. Empty unless analyzing a lambda call operator.
llvm::DenseMap<const ValueDecl*, NullabilityKind> CapturedVarNullability;
};
PointerNullabilityLatticeBase(NonFlowSensitiveState &NFS) : NFS(NFS) {}
const TypeNullability *absl_nullable getTypeNullability(
const Expr *absl_nonnull E) const {
auto I = NFS.ExprToNullability.find(&dataflow::ignoreCFGOmittedNodes(*E));
return I == NFS.ExprToNullability.end() ? nullptr : &I->second;
}
// Nonnull pointer fields to be modeled as nullable at destructor entry.
const llvm::DenseSet<const FieldDecl*>&
fieldsToTreatAsNullableAtDestructorEntry() const {
return NFS.FieldsToTreatAsNullableAtDestructorEntry;
}
// Flow-narrowed nullability of by-value captures to model at lambda
// call-operator entry.
const llvm::DenseMap<const ValueDecl*, NullabilityKind>&
capturedVarNullability() const {
return NFS.CapturedVarNullability;
}
/// Extract the nullability of the type of `D`.
///
/// The file where the type is written affects the interpretation of
/// unannotated pointer types. If the nullability for `D` has been overridden,
/// the returned nullability will contain these overrides.
TypeNullability getTypeNullabilityWithOverrides(
const ValueDecl &D,
llvm::function_ref<GetTypeParamNullability> SubstituteTypeParam =
nullptr) {
TypeNullability Nullability = clang::tidy::nullability::getTypeNullability(
D, defaults(), SubstituteTypeParam);
overrideNullabilityFromDecl(&D, Nullability);
return Nullability;
}
// If the `ExprToNullability` map already contains an entry for `E`, does
// nothing. Otherwise, inserts a new entry with key `E` and value computed by
// the provided GetNullability.
// Returns the (cached or computed) nullability.
const TypeNullability &insertExprNullabilityIfAbsent(
const Expr *absl_nonnull E,
const std::function<TypeNullability()> &GetNullability);
// If nullability for the decl D has been overridden, patch N to reflect it.
// (N is the nullability of an access to D).
void overrideNullabilityFromDecl(const Decl *absl_nullable D,
TypeNullability &N) const;
bool operator==(const PointerNullabilityLatticeBase &Other) const {
return true;
}
dataflow::LatticeJoinEffect join(const PointerNullabilityLatticeBase &Other);
const TypeNullabilityDefaults &defaults() const { return NFS.Defaults; }
private:
// Owned by the PointerNullabilityAnalysis object, shared by all lattice
// elements within one analysis run.
NonFlowSensitiveState &NFS;
};
using PointerNullabilityLattice =
dataflow::CachedConstAccessorsLattice<PointerNullabilityLatticeBase>;
inline std::ostream &operator<<(std::ostream &OS,
const PointerNullabilityLattice &) {
return OS << "nullability";
}
// If `E` accesses, through `*this`, a nonnull pointer field that is in the
// lattice's "treat as nullable at destructor entry" set, returns that field;
// otherwise returns nullptr.
const clang::FieldDecl* absl_nullable fieldTreatedAsNullableAtDestructorEntry(
const clang::Expr& E, const PointerNullabilityLatticeBase& Lattice);
// Returns true if `E` accesses, through `*this`, a nonnull pointer field that
// is in the lattice's "treat as nullable at destructor entry" set.
bool shouldTreatFieldAsNullableAtDestructorEntry(
const clang::Expr& E, const PointerNullabilityLatticeBase& Lattice);
// Flow-sensitive nullability of the by-value captured pointer read by `E`, if
// recorded at the capture site; otherwise std::nullopt.
std::optional<NullabilityKind> getCapturedVarNullability(
const clang::Expr& E, const PointerNullabilityLatticeBase& Lattice);
} // namespace clang::tidy::nullability
#endif // CRUBIT_NULLABILITY_POINTER_NULLABILITY_LATTICE_H_