| // 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 "absl/container/flat_hash_map.h" | |
| #include "absl/log/check.h" | |
| #include "nullability/type_nullability.h" | |
| #include "clang/AST/DeclCXX.h" | |
| #include "clang/AST/Expr.h" | |
| #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h" | |
| #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" | |
| #include "clang/Analysis/FlowSensitive/DataflowLattice.h" | |
| #include "clang/Analysis/FlowSensitive/StorageLocation.h" | |
| #include "clang/Analysis/FlowSensitive/Value.h" | |
| #include "clang/Basic/LLVM.h" | |
| #include "llvm/ADT/FunctionExtras.h" | |
| namespace clang::tidy::nullability { | |
| class PointerNullabilityLattice { | |
| public: | |
| struct NonFlowSensitiveState { | |
| // Nullability interpretation of types as set e.g. by per-file #pragmas. | |
| TypeNullabilityDefaults Defaults; | |
| absl::flat_hash_map<const Expr *, TypeNullability> ExprToNullability; | |
| // Overridden symbolic nullability for pointer-typed decls. | |
| // These are set by PointerNullabilityAnalysis::assignNullabilityVariable, | |
| // and take precedence over the declared type and over any result from | |
| // ConcreteNullabilityOverride. | |
| absl::flat_hash_map<absl::Nonnull<const ValueDecl *>, | |
| PointerTypeNullability> | |
| DeclTopLevelNullability; | |
| // Returns overriding concrete nullability for decls. This is set by | |
| // PointerNullabilityAnalysis::assignNullabilityOverride, and the result, if | |
| // present, takes precedence over the declared type. | |
| llvm::unique_function<std::optional<const PointerTypeNullability *>( | |
| const Decl &) const> | |
| ConcreteNullabilityOverride = [](const Decl &) { return std::nullopt; }; | |
| }; | |
| PointerNullabilityLattice(NonFlowSensitiveState &NFS) : NFS(NFS) {} | |
| absl::Nullable<const TypeNullability *> getTypeNullability( | |
| absl::Nonnull<const Expr *> E) const { | |
| auto I = NFS.ExprToNullability.find(&dataflow::ignoreCFGOmittedNodes(*E)); | |
| return I == NFS.ExprToNullability.end() ? nullptr : &I->second; | |
| } | |
| // 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( | |
| absl::Nonnull<const Expr *> E, | |
| const std::function<TypeNullability()> &GetNullability); | |
| // Returns the `Value` associated with the `RecordStorageLocation` and | |
| // `MethodDecl` of `CE`, creating one if it doesn't yet exist. | |
| // The type of `CE` must be either a raw pointer or boolean. | |
| absl::Nullable<dataflow::Value *> getConstMethodReturnValue( | |
| const dataflow::RecordStorageLocation &RecordLoc, | |
| absl::Nonnull<const CallExpr *> CE, dataflow::Environment &Env); | |
| // Returns the `RecordStorageLocation` associated with the | |
| // `RecordStorageLocation` and `MethodDecl` of `CE`, creating one if it | |
| // doesn't yet exist. The type of `CE` must be a smart pointer. | |
| absl::Nullable<dataflow::StorageLocation *> | |
| getConstMethodReturnStorageLocation( | |
| const dataflow::RecordStorageLocation &RecordLoc, | |
| absl::Nonnull<const CallExpr *> CE, dataflow::Environment &Env); | |
| void clearConstMethodReturnValues( | |
| const dataflow::RecordStorageLocation &RecordLoc) { | |
| ConstMethodReturnValues.erase(&RecordLoc); | |
| } | |
| void clearConstMethodReturnStorageLocations( | |
| const dataflow::RecordStorageLocation &RecordLoc) { | |
| ConstMethodReturnStorageLocations.erase(&RecordLoc); | |
| } | |
| // 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(absl::Nullable<const Decl *> D, | |
| TypeNullability &N) const; | |
| bool operator==(const PointerNullabilityLattice &Other) const { return true; } | |
| dataflow::LatticeJoinEffect join(const PointerNullabilityLattice &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; | |
| // Maps a record storage location and const method to the value to return | |
| // from that const method. | |
| using ConstMethodReturnValuesType = llvm::SmallDenseMap< | |
| const dataflow::RecordStorageLocation *, | |
| llvm::SmallDenseMap<const FunctionDecl *, dataflow::Value *>>; | |
| ConstMethodReturnValuesType ConstMethodReturnValues; | |
| // Maps a record storage location and const method to the record storage | |
| // location to return from that const method. | |
| using ConstMethodReturnStorageLocationsType = llvm::SmallDenseMap< | |
| const dataflow::RecordStorageLocation *, | |
| llvm::SmallDenseMap<const FunctionDecl *, dataflow::StorageLocation *>>; | |
| ConstMethodReturnStorageLocationsType ConstMethodReturnStorageLocations; | |
| }; | |
| inline std::ostream &operator<<(std::ostream &OS, | |
| const PointerNullabilityLattice &) { | |
| return OS << "noop"; | |
| } | |
| } // namespace clang::tidy::nullability | |
| #endif // CRUBIT_NULLABILITY_POINTER_NULLABILITY_LATTICE_H_ |