xref: /freebsd/contrib/llvm-project/clang/include/clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===-- SmartPointerAccessorCaching.h ---------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file defines utilities to help cache accessors for smart pointer
10 // like objects.
11 //
12 // These should be combined with CachedConstAccessorsLattice.
13 // Beyond basic const accessors, smart pointers may have the following two
14 // additional issues:
15 //
16 // 1) There may be multiple accessors for the same underlying object, e.g.
17 //    `operator->`, `operator*`, and `get`. Users may use a mixture of these
18 //    accessors, so the cache should unify them.
19 //
20 // 2) There may be non-const overloads of accessors. They are still safe to
21 //    cache, as they don't modify the container object.
22 //===----------------------------------------------------------------------===//
23 
24 #ifndef LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_SMARTPOINTERACCESSORCACHING_H
25 #define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_SMARTPOINTERACCESSORCACHING_H
26 
27 #include <cassert>
28 
29 #include "clang/AST/Decl.h"
30 #include "clang/AST/Expr.h"
31 #include "clang/AST/Stmt.h"
32 #include "clang/ASTMatchers/ASTMatchers.h"
33 #include "clang/Analysis/FlowSensitive/MatchSwitch.h"
34 #include "clang/Analysis/FlowSensitive/StorageLocation.h"
35 #include "clang/Analysis/FlowSensitive/Value.h"
36 #include "llvm/ADT/STLFunctionalExtras.h"
37 
38 namespace clang::dataflow {
39 
40 /// Matchers:
41 /// For now, these match on any class with an `operator*` or `operator->`
42 /// where the return types have a similar shape as std::unique_ptr
43 /// and std::optional.
44 ///
45 /// - `*` returns a reference to a type `T`
46 /// - `->` returns a pointer to `T`
47 /// - `get` returns a pointer to `T`
48 /// - `value` returns a reference `T`
49 ///
50 /// (1) The `T` should all match across the accessors (ignoring qualifiers).
51 ///
52 /// (2) The specific accessor used in a call isn't required to be const,
53 ///     but the class must have a const overload of each accessor.
54 ///
55 /// For now, we don't have customization to ignore certain classes.
56 /// For example, if writing a ClangTidy check for `std::optional`, these
57 /// would also match `std::optional`. In order to have special handling
58 /// for `std::optional`, we assume the (Matcher, TransferFunction) case
59 /// with custom handling is ordered early so that these generic cases
60 /// do not trigger.
61 ast_matchers::StatementMatcher isPointerLikeOperatorStar();
62 ast_matchers::StatementMatcher isSmartPointerLikeOperatorStar();
63 ast_matchers::StatementMatcher isPointerLikeOperatorArrow();
64 ast_matchers::StatementMatcher isSmartPointerLikeOperatorArrow();
65 ast_matchers::StatementMatcher
66 isSmartPointerLikeValueMethodCall(clang::StringRef MethodName = "value");
67 ast_matchers::StatementMatcher
68 isSmartPointerLikeGetMethodCall(clang::StringRef MethodName = "get");
69 
70 // Common transfer functions.
71 
72 /// Returns the "canonical" callee for smart pointer operators (`*` and `->`)
73 /// as a key for caching.
74 ///
75 /// We choose `*` as the canonical one, since it needs a
76 /// StorageLocation anyway.
77 ///
78 /// Note: there may be multiple `operator*` (one const, one non-const).
79 /// We pick the const one, which the above provided matchers require to exist.
80 const FunctionDecl *
81 getCanonicalSmartPointerLikeOperatorCallee(const CallExpr *CE);
82 
83 /// A transfer function for `operator*` (and `value`) calls that can be
84 /// cached. Runs the `InitializeLoc` callback to initialize any new
85 /// StorageLocations.
86 ///
87 /// Requirements:
88 ///
89 /// - LatticeT should use the `CachedConstAccessorsLattice` mixin.
90 template <typename LatticeT>
91 void transferSmartPointerLikeCachedDeref(
92     const CallExpr *DerefExpr, RecordStorageLocation *SmartPointerLoc,
93     TransferState<LatticeT> &State,
94     llvm::function_ref<void(StorageLocation &)> InitializeLoc);
95 
96 /// A transfer function for `operator->` (and `get`) calls that can be cached.
97 /// Runs the `InitializeLoc` callback to initialize any new StorageLocations.
98 ///
99 /// Requirements:
100 ///
101 /// - LatticeT should use the `CachedConstAccessorsLattice` mixin.
102 template <typename LatticeT>
103 void transferSmartPointerLikeCachedGet(
104     const CallExpr *GetExpr, RecordStorageLocation *SmartPointerLoc,
105     TransferState<LatticeT> &State,
106     llvm::function_ref<void(StorageLocation &)> InitializeLoc);
107 
108 template <typename LatticeT>
transferSmartPointerLikeCachedDeref(const CallExpr * DerefExpr,RecordStorageLocation * SmartPointerLoc,TransferState<LatticeT> & State,llvm::function_ref<void (StorageLocation &)> InitializeLoc)109 void transferSmartPointerLikeCachedDeref(
110     const CallExpr *DerefExpr, RecordStorageLocation *SmartPointerLoc,
111     TransferState<LatticeT> &State,
112     llvm::function_ref<void(StorageLocation &)> InitializeLoc) {
113   if (State.Env.getStorageLocation(*DerefExpr) != nullptr)
114     return;
115   if (SmartPointerLoc == nullptr)
116     return;
117 
118   const FunctionDecl *Callee = DerefExpr->getDirectCallee();
119   if (Callee == nullptr)
120     return;
121   const FunctionDecl *CanonicalCallee =
122       getCanonicalSmartPointerLikeOperatorCallee(DerefExpr);
123   // This shouldn't happen, as we should at least find `Callee` itself.
124   assert(CanonicalCallee != nullptr);
125   if (CanonicalCallee != Callee) {
126     // When using the provided matchers, we should always get a reference to
127     // the same type.
128     assert(CanonicalCallee->getReturnType()->isReferenceType() &&
129            Callee->getReturnType()->isReferenceType());
130     assert(CanonicalCallee->getReturnType()
131                .getNonReferenceType()
132                ->getCanonicalTypeUnqualified() ==
133            Callee->getReturnType()
134                .getNonReferenceType()
135                ->getCanonicalTypeUnqualified());
136   }
137 
138   StorageLocation &LocForValue =
139       State.Lattice.getOrCreateConstMethodReturnStorageLocation(
140           *SmartPointerLoc, CanonicalCallee, State.Env, InitializeLoc);
141   State.Env.setStorageLocation(*DerefExpr, LocForValue);
142 }
143 
144 template <typename LatticeT>
transferSmartPointerLikeCachedGet(const CallExpr * GetExpr,RecordStorageLocation * SmartPointerLoc,TransferState<LatticeT> & State,llvm::function_ref<void (StorageLocation &)> InitializeLoc)145 void transferSmartPointerLikeCachedGet(
146     const CallExpr *GetExpr, RecordStorageLocation *SmartPointerLoc,
147     TransferState<LatticeT> &State,
148     llvm::function_ref<void(StorageLocation &)> InitializeLoc) {
149   if (SmartPointerLoc == nullptr)
150     return;
151 
152   const FunctionDecl *CanonicalCallee =
153       getCanonicalSmartPointerLikeOperatorCallee(GetExpr);
154 
155   if (CanonicalCallee != nullptr) {
156     auto &LocForValue =
157         State.Lattice.getOrCreateConstMethodReturnStorageLocation(
158             *SmartPointerLoc, CanonicalCallee, State.Env, InitializeLoc);
159     State.Env.setValue(*GetExpr,
160                        State.Env.template create<PointerValue>(LocForValue));
161   } else {
162     // Otherwise, just cache the pointer value as if it was a const accessor.
163     Value *Val = State.Lattice.getOrCreateConstMethodReturnValue(
164         *SmartPointerLoc, GetExpr, State.Env);
165     if (Val == nullptr)
166       return;
167     State.Env.setValue(*GetExpr, *Val);
168   }
169 }
170 
171 } // namespace clang::dataflow
172 
173 #endif // LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_SMARTPOINTERACCESSORCACHING_H
174