Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-10 08:36:25

0001 //===-- SmartPointerAccessorCaching.h ---------------------------*- C++ -*-===//
0002 //
0003 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
0004 // See https://llvm.org/LICENSE.txt for license information.
0005 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
0006 //
0007 //===----------------------------------------------------------------------===//
0008 //
0009 // This file defines utilities to help cache accessors for smart pointer
0010 // like objects.
0011 //
0012 // These should be combined with CachedConstAccessorsLattice.
0013 // Beyond basic const accessors, smart pointers may have the following two
0014 // additional issues:
0015 //
0016 // 1) There may be multiple accessors for the same underlying object, e.g.
0017 //    `operator->`, `operator*`, and `get`. Users may use a mixture of these
0018 //    accessors, so the cache should unify them.
0019 //
0020 // 2) There may be non-const overloads of accessors. They are still safe to
0021 //    cache, as they don't modify the container object.
0022 //===----------------------------------------------------------------------===//
0023 
0024 #ifndef LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_SMARTPOINTERACCESSORCACHING_H
0025 #define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_SMARTPOINTERACCESSORCACHING_H
0026 
0027 #include <cassert>
0028 
0029 #include "clang/AST/Decl.h"
0030 #include "clang/AST/Expr.h"
0031 #include "clang/AST/Stmt.h"
0032 #include "clang/ASTMatchers/ASTMatchers.h"
0033 #include "clang/Analysis/FlowSensitive/MatchSwitch.h"
0034 #include "clang/Analysis/FlowSensitive/StorageLocation.h"
0035 #include "clang/Analysis/FlowSensitive/Value.h"
0036 #include "llvm/ADT/STLFunctionalExtras.h"
0037 
0038 namespace clang::dataflow {
0039 
0040 /// Matchers:
0041 /// For now, these match on any class with an `operator*` or `operator->`
0042 /// where the return types have a similar shape as std::unique_ptr
0043 /// and std::optional.
0044 ///
0045 /// - `*` returns a reference to a type `T`
0046 /// - `->` returns a pointer to `T`
0047 /// - `get` returns a pointer to `T`
0048 /// - `value` returns a reference `T`
0049 ///
0050 /// (1) The `T` should all match across the accessors (ignoring qualifiers).
0051 ///
0052 /// (2) The specific accessor used in a call isn't required to be const,
0053 ///     but the class must have a const overload of each accessor.
0054 ///
0055 /// For now, we don't have customization to ignore certain classes.
0056 /// For example, if writing a ClangTidy check for `std::optional`, these
0057 /// would also match `std::optional`. In order to have special handling
0058 /// for `std::optional`, we assume the (Matcher, TransferFunction) case
0059 /// with custom handling is ordered early so that these generic cases
0060 /// do not trigger.
0061 ast_matchers::StatementMatcher isSmartPointerLikeOperatorStar();
0062 ast_matchers::StatementMatcher isSmartPointerLikeOperatorArrow();
0063 ast_matchers::StatementMatcher isSmartPointerLikeValueMethodCall();
0064 ast_matchers::StatementMatcher isSmartPointerLikeGetMethodCall();
0065 
0066 // Common transfer functions.
0067 
0068 /// Returns the "canonical" callee for smart pointer operators (`*` and `->`)
0069 /// as a key for caching.
0070 ///
0071 /// We choose `*` as the canonical one, since it needs a
0072 /// StorageLocation anyway.
0073 ///
0074 /// Note: there may be multiple `operator*` (one const, one non-const).
0075 /// We pick the const one, which the above provided matchers require to exist.
0076 const FunctionDecl *
0077 getCanonicalSmartPointerLikeOperatorCallee(const CallExpr *CE);
0078 
0079 /// A transfer function for `operator*` (and `value`) calls that can be
0080 /// cached. Runs the `InitializeLoc` callback to initialize any new
0081 /// StorageLocations.
0082 ///
0083 /// Requirements:
0084 ///
0085 /// - LatticeT should use the `CachedConstAccessorsLattice` mixin.
0086 template <typename LatticeT>
0087 void transferSmartPointerLikeCachedDeref(
0088     const CallExpr *DerefExpr, RecordStorageLocation *SmartPointerLoc,
0089     TransferState<LatticeT> &State,
0090     llvm::function_ref<void(StorageLocation &)> InitializeLoc);
0091 
0092 /// A transfer function for `operator->` (and `get`) calls that can be cached.
0093 /// Runs the `InitializeLoc` callback to initialize any new StorageLocations.
0094 ///
0095 /// Requirements:
0096 ///
0097 /// - LatticeT should use the `CachedConstAccessorsLattice` mixin.
0098 template <typename LatticeT>
0099 void transferSmartPointerLikeCachedGet(
0100     const CallExpr *GetExpr, RecordStorageLocation *SmartPointerLoc,
0101     TransferState<LatticeT> &State,
0102     llvm::function_ref<void(StorageLocation &)> InitializeLoc);
0103 
0104 template <typename LatticeT>
0105 void transferSmartPointerLikeCachedDeref(
0106     const CallExpr *DerefExpr, RecordStorageLocation *SmartPointerLoc,
0107     TransferState<LatticeT> &State,
0108     llvm::function_ref<void(StorageLocation &)> InitializeLoc) {
0109   if (State.Env.getStorageLocation(*DerefExpr) != nullptr)
0110     return;
0111   if (SmartPointerLoc == nullptr)
0112     return;
0113 
0114   const FunctionDecl *Callee = DerefExpr->getDirectCallee();
0115   if (Callee == nullptr)
0116     return;
0117   const FunctionDecl *CanonicalCallee =
0118       getCanonicalSmartPointerLikeOperatorCallee(DerefExpr);
0119   // This shouldn't happen, as we should at least find `Callee` itself.
0120   assert(CanonicalCallee != nullptr);
0121   if (CanonicalCallee != Callee) {
0122     // When using the provided matchers, we should always get a reference to
0123     // the same type.
0124     assert(CanonicalCallee->getReturnType()->isReferenceType() &&
0125            Callee->getReturnType()->isReferenceType());
0126     assert(CanonicalCallee->getReturnType()
0127                .getNonReferenceType()
0128                ->getCanonicalTypeUnqualified() ==
0129            Callee->getReturnType()
0130                .getNonReferenceType()
0131                ->getCanonicalTypeUnqualified());
0132   }
0133 
0134   StorageLocation &LocForValue =
0135       State.Lattice.getOrCreateConstMethodReturnStorageLocation(
0136           *SmartPointerLoc, CanonicalCallee, State.Env, InitializeLoc);
0137   State.Env.setStorageLocation(*DerefExpr, LocForValue);
0138 }
0139 
0140 template <typename LatticeT>
0141 void transferSmartPointerLikeCachedGet(
0142     const CallExpr *GetExpr, RecordStorageLocation *SmartPointerLoc,
0143     TransferState<LatticeT> &State,
0144     llvm::function_ref<void(StorageLocation &)> InitializeLoc) {
0145   if (SmartPointerLoc == nullptr)
0146     return;
0147 
0148   const FunctionDecl *CanonicalCallee =
0149       getCanonicalSmartPointerLikeOperatorCallee(GetExpr);
0150 
0151   if (CanonicalCallee != nullptr) {
0152     auto &LocForValue =
0153         State.Lattice.getOrCreateConstMethodReturnStorageLocation(
0154             *SmartPointerLoc, CanonicalCallee, State.Env, InitializeLoc);
0155     State.Env.setValue(*GetExpr,
0156                        State.Env.template create<PointerValue>(LocForValue));
0157   } else {
0158     // Otherwise, just cache the pointer value as if it was a const accessor.
0159     Value *Val = State.Lattice.getOrCreateConstMethodReturnValue(
0160         *SmartPointerLoc, GetExpr, State.Env);
0161     if (Val == nullptr)
0162       return;
0163     State.Env.setValue(*GetExpr, *Val);
0164   }
0165 }
0166 
0167 } // namespace clang::dataflow
0168 
0169 #endif // LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_SMARTPOINTERACCESSORCACHING_H