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