File indexing completed on 2024-04-21 05:38:47
0001 /* 0002 SPDX-FileCopyrightText: 2015 Klarälvdalens Datakonsult AB a KDAB Group company info@kdab.com 0003 SPDX-FileContributor: Sérgio Martins <sergio.martins@kdab.com> 0004 0005 SPDX-FileCopyrightText: 2015 Sergio Martins <smartins@kde.org> 0006 0007 SPDX-License-Identifier: LGPL-2.0-or-later 0008 */ 0009 0010 #ifndef CLAZY_HIERARCHY_UTILS_H 0011 #define CLAZY_HIERARCHY_UTILS_H 0012 0013 // Contains utility classes to retrieve parents and childs from AST Nodes 0014 0015 #include "StringUtils.h" 0016 #include "clazy_stl.h" 0017 0018 #include <clang/AST/ExprCXX.h> 0019 #include <clang/AST/ParentMap.h> 0020 #include <clang/AST/Stmt.h> 0021 #include <clang/Frontend/CompilerInstance.h> 0022 0023 namespace clazy 0024 { 0025 enum IgnoreStmt { IgnoreNone = 0, IgnoreImplicitCasts = 1, IgnoreExprWithCleanups = 2 }; 0026 0027 using IgnoreStmts = int; 0028 0029 /** 0030 * Returns true if child is a child of parent. 0031 */ 0032 inline bool isChildOf(clang::Stmt *child, clang::Stmt *parent) 0033 { 0034 if (!child || !parent) { 0035 return false; 0036 } 0037 0038 return clazy::any_of(parent->children(), [child](clang::Stmt *c) { 0039 return c == child || isChildOf(child, c); 0040 }); 0041 } 0042 0043 /** 0044 * Returns true if stm is parent of a member function call named "name" 0045 */ 0046 0047 inline bool isParentOfMemberFunctionCall(clang::Stmt *stm, const std::string &name) 0048 { 0049 if (!stm) { 0050 return false; 0051 } 0052 0053 if (auto *expr = llvm::dyn_cast<clang::MemberExpr>(stm)) { 0054 auto *namedDecl = llvm::dyn_cast<clang::NamedDecl>(expr->getMemberDecl()); 0055 if (namedDecl && clazy::name(namedDecl) == name) { 0056 return true; 0057 } 0058 } 0059 0060 return clazy::any_of(stm->children(), [name](clang::Stmt *child) { 0061 return isParentOfMemberFunctionCall(child, name); 0062 }); 0063 0064 return false; 0065 } 0066 0067 /** 0068 * Returns the first child of stm of type T. 0069 * Does depth-first. 0070 */ 0071 template<typename T> 0072 T *getFirstChildOfType(clang::Stmt *stm) 0073 { 0074 if (!stm) { 0075 return nullptr; 0076 } 0077 0078 for (auto *child : stm->children()) { 0079 if (!child) { // Can happen 0080 continue; 0081 } 0082 0083 if (auto s = clang::dyn_cast<T>(child)) { 0084 return s; 0085 } 0086 0087 if (auto s = getFirstChildOfType<T>(child)) { 0088 return s; 0089 } 0090 } 0091 0092 return nullptr; 0093 } 0094 0095 /** 0096 * Returns the first child of stm of type T, but only looks at the first branch. 0097 */ 0098 template<typename T> 0099 T *getFirstChildOfType2(clang::Stmt *stm) 0100 { 0101 if (!stm) { 0102 return nullptr; 0103 } 0104 0105 if (clazy::hasChildren(stm)) { 0106 auto *child = *(stm->child_begin()); 0107 0108 if (!child) { // can happen 0109 return nullptr; 0110 } 0111 0112 if (auto s = clang::dyn_cast<T>(child)) { 0113 return s; 0114 } 0115 0116 if (auto s = getFirstChildOfType<T>(child)) { 0117 return s; 0118 } 0119 } 0120 0121 return nullptr; 0122 } 0123 0124 // If depth = 0, return s 0125 // If depth = 1, returns parent of s 0126 // etc. 0127 inline clang::Stmt *parent(clang::ParentMap *map, clang::Stmt *s, unsigned int depth = 1) 0128 { 0129 if (!s) { 0130 return nullptr; 0131 } 0132 0133 return depth == 0 ? s : clazy::parent(map, map->getParent(s), depth - 1); 0134 } 0135 0136 // Returns the first parent of type T, with max depth depth 0137 template<typename T> 0138 T *getFirstParentOfType(clang::ParentMap *pmap, clang::Stmt *s, unsigned int depth = -1) 0139 { 0140 if (!s) { 0141 return nullptr; 0142 } 0143 0144 if (auto t = clang::dyn_cast<T>(s)) { 0145 return t; 0146 } 0147 0148 if (depth == 0) { 0149 return nullptr; 0150 } 0151 0152 --depth; 0153 return getFirstParentOfType<T>(pmap, parent(pmap, s), depth); 0154 } 0155 0156 inline clang::Stmt *getFirstChild(clang::Stmt *parent) 0157 { 0158 if (!parent) { 0159 return nullptr; 0160 } 0161 0162 auto it = parent->child_begin(); 0163 return it == parent->child_end() ? nullptr : *it; 0164 } 0165 0166 inline clang::Stmt *getFirstChildAtDepth(clang::Stmt *s, unsigned int depth) 0167 { 0168 if (depth == 0 || !s) { 0169 return s; 0170 } 0171 0172 return clazy::hasChildren(s) ? getFirstChildAtDepth(*s->child_begin(), --depth) : nullptr; 0173 } 0174 0175 template<typename T> 0176 void getChilds(clang::Stmt *stmt, std::vector<T *> &result_list, int depth = -1) 0177 { 0178 if (!stmt) { 0179 return; 0180 } 0181 0182 auto cexpr = llvm::dyn_cast<T>(stmt); 0183 if (cexpr) { 0184 result_list.push_back(cexpr); 0185 } 0186 0187 if (depth > 0 || depth == -1) { 0188 if (depth > 0) { 0189 --depth; 0190 } 0191 for (auto *child : stmt->children()) { 0192 getChilds(child, result_list, depth); 0193 } 0194 } 0195 } 0196 0197 inline bool isIgnoredByOption(clang::Stmt *s, IgnoreStmts options) 0198 { 0199 return ((options & IgnoreImplicitCasts) && llvm::isa<clang::ImplicitCastExpr>(s)) 0200 || ((options & IgnoreExprWithCleanups) && llvm::isa<clang::ExprWithCleanups>(s)); 0201 } 0202 0203 /** 0204 * Returns all statements of type T in body, starting from startLocation, or from body->getLocStart() if 0205 * startLocation is null. 0206 * 0207 * Similar to getChilds(), but with startLocation support. 0208 */ 0209 template<typename T> 0210 std::vector<T *> getStatements(clang::Stmt *body, 0211 const clang::SourceManager *sm = nullptr, 0212 clang::SourceLocation startLocation = {}, 0213 int depth = -1, 0214 bool includeParent = false, 0215 IgnoreStmts ignoreOptions = IgnoreNone) 0216 { 0217 std::vector<T *> statements; 0218 if (!body || depth == 0) { 0219 return statements; 0220 } 0221 0222 if (includeParent) { 0223 if (T *t = clang::dyn_cast<T>(body)) { 0224 statements.push_back(t); 0225 } 0226 } 0227 0228 for (auto *child : body->children()) { 0229 if (!child) { 0230 continue; // can happen 0231 } 0232 if (T *childT = clang::dyn_cast<T>(child)) { 0233 if (!startLocation.isValid() || (sm && sm->isBeforeInSLocAddrSpace(sm->getSpellingLoc(startLocation), child->getBeginLoc()))) { 0234 statements.push_back(childT); 0235 } 0236 } 0237 0238 if (!isIgnoredByOption(child, ignoreOptions)) { 0239 --depth; 0240 } 0241 0242 auto childStatements = getStatements<T>(child, sm, startLocation, depth, false, ignoreOptions); 0243 clazy::append(childStatements, statements); 0244 } 0245 0246 return statements; 0247 } 0248 0249 /** 0250 * If stmt is of type T, then stmt is returned. 0251 * If stmt is of type IgnoreImplicitCast or IgnoreExprWithCleanups (depending on options) then stmt's 0252 * first child is tested instead (recurses). 0253 * Otherwise nullptr is returned. 0254 * 0255 * This is useful for example when the interesting statement is under an Implicit cast, so: 0256 **/ 0257 template<typename T> 0258 T *unpeal(clang::Stmt *stmt, IgnoreStmts options = IgnoreNone) 0259 { 0260 if (!stmt) { 0261 return nullptr; 0262 } 0263 0264 if (auto tt = llvm::dyn_cast<T>(stmt)) { 0265 return tt; 0266 } 0267 0268 if ((options & IgnoreImplicitCasts) && llvm::isa<clang::ImplicitCastExpr>(stmt)) { 0269 return unpeal<T>(clazy::getFirstChild(stmt), options); 0270 } 0271 0272 if ((options & IgnoreExprWithCleanups) && llvm::isa<clang::ExprWithCleanups>(stmt)) { 0273 return unpeal<T>(clazy::getFirstChild(stmt), options); 0274 } 0275 0276 return nullptr; 0277 } 0278 0279 inline clang::SwitchStmt *getSwitchFromCase(clang::ParentMap *pmap, clang::CaseStmt *caseStm) 0280 { 0281 return getFirstParentOfType<clang::SwitchStmt>(pmap, caseStm); 0282 } 0283 0284 } 0285 0286 #endif