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