xref: /llvm-project/clang/lib/Analysis/FlowSensitive/ASTOps.cpp (revision 5161a3f6e5e92c78c33aed5e38e0680a1a9b088e)
1 //===-- ASTOps.cc -------------------------------*- 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 //  Operations on AST nodes that are used in flow-sensitive analysis.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "clang/Analysis/FlowSensitive/ASTOps.h"
14 #include "clang/AST/ComputeDependence.h"
15 #include "clang/AST/Decl.h"
16 #include "clang/AST/DeclBase.h"
17 #include "clang/AST/DeclCXX.h"
18 #include "clang/AST/Expr.h"
19 #include "clang/AST/ExprCXX.h"
20 #include "clang/AST/Stmt.h"
21 #include "clang/AST/Type.h"
22 #include "clang/Analysis/FlowSensitive/StorageLocation.h"
23 #include "clang/Basic/LLVM.h"
24 #include "llvm/ADT/DenseSet.h"
25 #include "llvm/ADT/STLExtras.h"
26 #include <cassert>
27 #include <iterator>
28 #include <vector>
29 
30 #define DEBUG_TYPE "dataflow"
31 
32 namespace clang::dataflow {
33 
34 const Expr &ignoreCFGOmittedNodes(const Expr &E) {
35   const Expr *Current = &E;
36   const Expr *Last = nullptr;
37   while (Current != Last) {
38     Last = Current;
39     if (auto *EWC = dyn_cast<ExprWithCleanups>(Current)) {
40       Current = EWC->getSubExpr();
41       assert(Current != nullptr);
42     }
43     if (auto *CE = dyn_cast<ConstantExpr>(Current)) {
44       Current = CE->getSubExpr();
45       assert(Current != nullptr);
46     }
47     Current = Current->IgnoreParens();
48     assert(Current != nullptr);
49   }
50   return *Current;
51 }
52 
53 const Stmt &ignoreCFGOmittedNodes(const Stmt &S) {
54   if (auto *E = dyn_cast<Expr>(&S))
55     return ignoreCFGOmittedNodes(*E);
56   return S;
57 }
58 
59 // FIXME: Does not precisely handle non-virtual diamond inheritance. A single
60 // field decl will be modeled for all instances of the inherited field.
61 static void getFieldsFromClassHierarchy(QualType Type, FieldSet &Fields) {
62   if (Type->isIncompleteType() || Type->isDependentType() ||
63       !Type->isRecordType())
64     return;
65 
66   for (const FieldDecl *Field : Type->getAsRecordDecl()->fields())
67     Fields.insert(Field);
68   if (auto *CXXRecord = Type->getAsCXXRecordDecl())
69     for (const CXXBaseSpecifier &Base : CXXRecord->bases())
70       getFieldsFromClassHierarchy(Base.getType(), Fields);
71 }
72 
73 /// Gets the set of all fields in the type.
74 FieldSet getObjectFields(QualType Type) {
75   FieldSet Fields;
76   getFieldsFromClassHierarchy(Type, Fields);
77   return Fields;
78 }
79 
80 bool containsSameFields(const FieldSet &Fields,
81                         const RecordStorageLocation::FieldToLoc &FieldLocs) {
82   if (Fields.size() != FieldLocs.size())
83     return false;
84   for ([[maybe_unused]] auto [Field, Loc] : FieldLocs)
85     if (!Fields.contains(cast_or_null<FieldDecl>(Field)))
86       return false;
87   return true;
88 }
89 
90 /// Returns the fields of a `RecordDecl` that are initialized by an
91 /// `InitListExpr` or `CXXParenListInitExpr`, in the order in which they appear
92 /// in `InitListExpr::inits()` / `CXXParenListInitExpr::getInitExprs()`.
93 /// `InitList->getType()` must be a record type.
94 template <class InitListT>
95 static std::vector<const FieldDecl *>
96 getFieldsForInitListExpr(const InitListT *InitList) {
97   const RecordDecl *RD = InitList->getType()->getAsRecordDecl();
98   assert(RD != nullptr);
99 
100   std::vector<const FieldDecl *> Fields;
101 
102   if (InitList->getType()->isUnionType()) {
103     Fields.push_back(InitList->getInitializedFieldInUnion());
104     return Fields;
105   }
106 
107   // Unnamed bitfields are only used for padding and do not appear in
108   // `InitListExpr`'s inits. However, those fields do appear in `RecordDecl`'s
109   // field list, and we thus need to remove them before mapping inits to
110   // fields to avoid mapping inits to the wrongs fields.
111   llvm::copy_if(
112       RD->fields(), std::back_inserter(Fields),
113       [](const FieldDecl *Field) { return !Field->isUnnamedBitField(); });
114   return Fields;
115 }
116 
117 RecordInitListHelper::RecordInitListHelper(const InitListExpr *InitList)
118     : RecordInitListHelper(InitList->getType(),
119                            getFieldsForInitListExpr(InitList),
120                            InitList->inits()) {}
121 
122 RecordInitListHelper::RecordInitListHelper(
123     const CXXParenListInitExpr *ParenInitList)
124     : RecordInitListHelper(ParenInitList->getType(),
125                            getFieldsForInitListExpr(ParenInitList),
126                            ParenInitList->getInitExprs()) {}
127 
128 RecordInitListHelper::RecordInitListHelper(
129     QualType Ty, std::vector<const FieldDecl *> Fields,
130     ArrayRef<Expr *> Inits) {
131   auto *RD = Ty->getAsCXXRecordDecl();
132   assert(RD != nullptr);
133 
134   // Unions initialized with an empty initializer list need special treatment.
135   // For structs/classes initialized with an empty initializer list, Clang
136   // puts `ImplicitValueInitExpr`s in `InitListExpr::inits()`, but for unions,
137   // it doesn't do this -- so we create an `ImplicitValueInitExpr` ourselves.
138   SmallVector<Expr *> InitsForUnion;
139   if (Ty->isUnionType() && Inits.empty()) {
140     assert(Fields.size() == 1);
141     ImplicitValueInitForUnion.emplace(Fields.front()->getType());
142     InitsForUnion.push_back(&*ImplicitValueInitForUnion);
143     Inits = InitsForUnion;
144   }
145 
146   size_t InitIdx = 0;
147 
148   assert(Fields.size() + RD->getNumBases() == Inits.size());
149   for (const CXXBaseSpecifier &Base : RD->bases()) {
150     assert(InitIdx < Inits.size());
151     Expr *Init = Inits[InitIdx++];
152     BaseInits.emplace_back(&Base, Init);
153   }
154 
155   assert(Fields.size() == Inits.size() - InitIdx);
156   for (const FieldDecl *Field : Fields) {
157     assert(InitIdx < Inits.size());
158     Expr *Init = Inits[InitIdx++];
159     FieldInits.emplace_back(Field, Init);
160   }
161 }
162 
163 static void insertIfGlobal(const Decl &D,
164                            llvm::DenseSet<const VarDecl *> &Globals) {
165   if (auto *V = dyn_cast<VarDecl>(&D))
166     if (V->hasGlobalStorage())
167       Globals.insert(V);
168 }
169 
170 static void insertIfFunction(const Decl &D,
171                              llvm::DenseSet<const FunctionDecl *> &Funcs) {
172   if (auto *FD = dyn_cast<FunctionDecl>(&D))
173     Funcs.insert(FD);
174 }
175 
176 static MemberExpr *getMemberForAccessor(const CXXMemberCallExpr &C) {
177   // Use getCalleeDecl instead of getMethodDecl in order to handle
178   // pointer-to-member calls.
179   const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(C.getCalleeDecl());
180   if (!MethodDecl)
181     return nullptr;
182   auto *Body = dyn_cast_or_null<CompoundStmt>(MethodDecl->getBody());
183   if (!Body || Body->size() != 1)
184     return nullptr;
185   if (auto *RS = dyn_cast<ReturnStmt>(*Body->body_begin()))
186     if (auto *Return = RS->getRetValue())
187       return dyn_cast<MemberExpr>(Return->IgnoreParenImpCasts());
188   return nullptr;
189 }
190 
191 class ReferencedDeclsVisitor
192     : public AnalysisASTVisitor<ReferencedDeclsVisitor> {
193 public:
194   ReferencedDeclsVisitor(ReferencedDecls &Referenced)
195       : Referenced(Referenced) {}
196 
197   void TraverseConstructorInits(const CXXConstructorDecl *Ctor) {
198     for (const CXXCtorInitializer *Init : Ctor->inits()) {
199       if (Init->isMemberInitializer()) {
200         Referenced.Fields.insert(Init->getMember());
201       } else if (Init->isIndirectMemberInitializer()) {
202         for (const auto *I : Init->getIndirectMember()->chain())
203           Referenced.Fields.insert(cast<FieldDecl>(I));
204       }
205 
206       Expr *InitExpr = Init->getInit();
207 
208       // Also collect declarations referenced in `InitExpr`.
209       TraverseStmt(InitExpr);
210 
211       // If this is a `CXXDefaultInitExpr`, also collect declarations referenced
212       // within the default expression.
213       if (auto *DefaultInit = dyn_cast<CXXDefaultInitExpr>(InitExpr))
214         TraverseStmt(DefaultInit->getExpr());
215     }
216   }
217 
218   bool VisitDecl(Decl *D) {
219     insertIfGlobal(*D, Referenced.Globals);
220     insertIfFunction(*D, Referenced.Functions);
221     return true;
222   }
223 
224   bool VisitDeclRefExpr(DeclRefExpr *E) {
225     insertIfGlobal(*E->getDecl(), Referenced.Globals);
226     insertIfFunction(*E->getDecl(), Referenced.Functions);
227     return true;
228   }
229 
230   bool VisitCXXMemberCallExpr(CXXMemberCallExpr *C) {
231     // If this is a method that returns a member variable but does nothing else,
232     // model the field of the return value.
233     if (MemberExpr *E = getMemberForAccessor(*C))
234       if (const auto *FD = dyn_cast<FieldDecl>(E->getMemberDecl()))
235         Referenced.Fields.insert(FD);
236     return true;
237   }
238 
239   bool VisitMemberExpr(MemberExpr *E) {
240     // FIXME: should we be using `E->getFoundDecl()`?
241     const ValueDecl *VD = E->getMemberDecl();
242     insertIfGlobal(*VD, Referenced.Globals);
243     insertIfFunction(*VD, Referenced.Functions);
244     if (const auto *FD = dyn_cast<FieldDecl>(VD))
245       Referenced.Fields.insert(FD);
246     return true;
247   }
248 
249   bool VisitInitListExpr(InitListExpr *InitList) {
250     if (InitList->getType()->isRecordType())
251       for (const auto *FD : getFieldsForInitListExpr(InitList))
252         Referenced.Fields.insert(FD);
253     return true;
254   }
255 
256   bool VisitCXXParenListInitExpr(CXXParenListInitExpr *ParenInitList) {
257     if (ParenInitList->getType()->isRecordType())
258       for (const auto *FD : getFieldsForInitListExpr(ParenInitList))
259         Referenced.Fields.insert(FD);
260     return true;
261   }
262 
263 private:
264   ReferencedDecls &Referenced;
265 };
266 
267 ReferencedDecls getReferencedDecls(const FunctionDecl &FD) {
268   ReferencedDecls Result;
269   ReferencedDeclsVisitor Visitor(Result);
270   Visitor.TraverseStmt(FD.getBody());
271   if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(&FD))
272     Visitor.TraverseConstructorInits(CtorDecl);
273 
274   return Result;
275 }
276 
277 ReferencedDecls getReferencedDecls(const Stmt &S) {
278   ReferencedDecls Result;
279   ReferencedDeclsVisitor Visitor(Result);
280   Visitor.TraverseStmt(const_cast<Stmt *>(&S));
281   return Result;
282 }
283 
284 } // namespace clang::dataflow
285