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