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