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