File indexing completed on 2024-04-14 05:32:09

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     SPDX-FileCopyrightText: 2015-2016 Sergio Martins <smartins@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "Utils.h"
0010 #include "HierarchyUtils.h"
0011 #include "SourceCompatibilityHelpers.h"
0012 #include "StmtBodyRange.h"
0013 #include "StringUtils.h"
0014 #include "clazy_stl.h"
0015 
0016 #include <clang/AST/Decl.h>
0017 #include <clang/AST/DeclBase.h>
0018 #include <clang/AST/DeclCXX.h>
0019 #include <clang/AST/DeclGroup.h>
0020 #include <clang/AST/DeclTemplate.h>
0021 #include <clang/AST/Expr.h>
0022 #include <clang/AST/ExprCXX.h>
0023 #include <clang/AST/OperationKinds.h>
0024 #include <clang/AST/Stmt.h>
0025 #include <clang/AST/StmtIterator.h>
0026 #include <clang/AST/Type.h>
0027 #include <clang/Basic/CharInfo.h>
0028 #include <clang/Basic/IdentifierTable.h>
0029 #include <clang/Basic/LLVM.h>
0030 #include <clang/Basic/SourceLocation.h>
0031 #include <clang/Basic/SourceManager.h>
0032 #include <clang/Lex/Lexer.h>
0033 #include <clang/Lex/Token.h>
0034 #include <llvm/Support/Casting.h>
0035 
0036 #include <cctype>
0037 #include <iterator>
0038 #include <utility>
0039 
0040 namespace clang
0041 {
0042 class LangOptions;
0043 } // namespace clang
0044 
0045 using namespace clang;
0046 
0047 bool Utils::hasConstexprCtor(CXXRecordDecl *decl)
0048 {
0049     return clazy::any_of(decl->ctors(), [](CXXConstructorDecl *ctor) {
0050         return ctor->isConstexpr();
0051     });
0052 }
0053 
0054 CXXRecordDecl *Utils::namedCastInnerDecl(CXXNamedCastExpr *staticOrDynamicCast)
0055 {
0056     Expr *e = staticOrDynamicCast->getSubExpr();
0057     if (!e) {
0058         return nullptr;
0059     }
0060     if (auto *implicitCast = dyn_cast<ImplicitCastExpr>(e)) {
0061         // Sometimes it's automatically cast to base
0062         if (implicitCast->getCastKind() == CK_DerivedToBase) {
0063             e = implicitCast->getSubExpr();
0064         }
0065     }
0066 
0067     QualType qt = e->getType();
0068     const Type *t = qt.getTypePtrOrNull();
0069     if (!t) {
0070         return nullptr;
0071     }
0072     QualType qt2 = t->getPointeeType();
0073     const Type *t2 = qt2.getTypePtrOrNull();
0074     if (!t2) {
0075         return nullptr;
0076     }
0077     return t2->getAsCXXRecordDecl();
0078 }
0079 
0080 CXXRecordDecl *Utils::namedCastOuterDecl(CXXNamedCastExpr *staticOrDynamicCast)
0081 {
0082     QualType qt = staticOrDynamicCast->getTypeAsWritten();
0083     const Type *t = qt.getTypePtrOrNull();
0084     QualType qt2 = t->getPointeeType();
0085     const Type *t2 = qt2.getTypePtrOrNull();
0086     if (!t2) {
0087         return nullptr;
0088     }
0089     return t2->getAsCXXRecordDecl();
0090 }
0091 
0092 bool Utils::allChildrenMemberCallsConst(Stmt *stm)
0093 {
0094     if (!stm) {
0095         return false;
0096     }
0097 
0098     auto *expr = dyn_cast<MemberExpr>(stm);
0099 
0100     if (expr) {
0101         auto *methodDecl = dyn_cast<CXXMethodDecl>(expr->getMemberDecl());
0102         if (methodDecl && !methodDecl->isConst()) {
0103             return false;
0104         }
0105     }
0106 
0107     return clazy::all_of(stm->children(), [](Stmt *child) {
0108         return allChildrenMemberCallsConst(child);
0109     });
0110 }
0111 
0112 bool Utils::childsHaveSideEffects(Stmt *stm)
0113 {
0114     if (!stm) {
0115         return false;
0116     }
0117 
0118     auto *unary = dyn_cast<UnaryOperator>(stm);
0119     if (unary && (unary->isIncrementOp() || unary->isDecrementOp())) {
0120         return true;
0121     }
0122 
0123     auto *binary = dyn_cast<BinaryOperator>(stm);
0124     if (binary && (binary->isAssignmentOp() || binary->isShiftAssignOp() || binary->isCompoundAssignmentOp())) {
0125         return true;
0126     }
0127 
0128     static const std::vector<StringRef> method_blacklist = {
0129         "isDestroyed",
0130         "isRecursive", // TODO: Use qualified name instead ?
0131         "q_func",
0132         "d_func",
0133         "begin",
0134         "end",
0135         "data",
0136         "fragment",
0137         "glIsRenderbuffer",
0138     };
0139 
0140     auto *memberCall = dyn_cast<MemberExpr>(stm);
0141     if (memberCall) {
0142         auto *methodDecl = dyn_cast<CXXMethodDecl>(memberCall->getMemberDecl());
0143         if (methodDecl && !methodDecl->isConst() && !methodDecl->isStatic() && !clazy::contains(method_blacklist, clazy::name(methodDecl))) {
0144             return true;
0145         }
0146     }
0147 
0148     /* // too many false positives, qIsFinite() etc for example
0149     auto callExpr = dyn_cast<CallExpr>(stm);
0150     if (callExpr) {
0151         FunctionDecl *callee = callExpr->getDirectCallee();
0152         if (callee && callee->isGlobal())
0153             return true;
0154     }*/
0155 
0156     return clazy::any_of(stm->children(), [](Stmt *s) {
0157         return childsHaveSideEffects(s);
0158     });
0159 }
0160 
0161 CXXRecordDecl *Utils::recordFromVarDecl(Decl *decl)
0162 {
0163     auto *varDecl = dyn_cast<VarDecl>(decl);
0164     if (!varDecl) {
0165         return nullptr;
0166     }
0167 
0168     QualType qt = varDecl->getType();
0169     const Type *t = qt.getTypePtrOrNull();
0170     if (!t) {
0171         return nullptr;
0172     }
0173 
0174     return t->getAsCXXRecordDecl();
0175 }
0176 
0177 ClassTemplateSpecializationDecl *Utils::templateSpecializationFromVarDecl(Decl *decl)
0178 {
0179     auto *record = recordFromVarDecl(decl);
0180     if (record) {
0181         return dyn_cast<ClassTemplateSpecializationDecl>(record);
0182     }
0183 
0184     return nullptr;
0185 }
0186 
0187 ValueDecl *Utils::valueDeclForMemberCall(CXXMemberCallExpr *memberCall)
0188 {
0189     if (!memberCall) {
0190         return nullptr;
0191     }
0192 
0193     Expr *implicitObject = memberCall->getImplicitObjectArgument();
0194     if (!implicitObject) {
0195         return nullptr;
0196     }
0197 
0198     auto *declRefExpr = dyn_cast<DeclRefExpr>(implicitObject);
0199     auto *memberExpr = dyn_cast<MemberExpr>(implicitObject);
0200     if (declRefExpr) {
0201         return declRefExpr->getDecl();
0202     }
0203     if (memberExpr) {
0204         return memberExpr->getMemberDecl();
0205     }
0206 
0207     // Maybe there's an implicit cast in between..
0208     auto memberExprs = clazy::getStatements<MemberExpr>(implicitObject, nullptr, {}, /**depth=*/1, /*includeParent=*/true);
0209     auto declRefs = clazy::getStatements<DeclRefExpr>(implicitObject, nullptr, {}, /**depth=*/1, /*includeParent=*/true);
0210 
0211     if (!memberExprs.empty()) {
0212         return memberExprs.at(0)->getMemberDecl();
0213     }
0214 
0215     if (!declRefs.empty()) {
0216         return declRefs.at(0)->getDecl();
0217     }
0218 
0219     return nullptr;
0220 }
0221 
0222 ValueDecl *Utils::valueDeclForOperatorCall(CXXOperatorCallExpr *operatorCall)
0223 {
0224     if (!operatorCall) {
0225         return nullptr;
0226     }
0227 
0228     // CXXOperatorCallExpr doesn't have API to access the value decl.
0229     // By inspecting several ASTs I noticed it's always in the 2nd child
0230 
0231     Stmt *child2 = clazy::childAt(operatorCall, 1);
0232     if (!child2) {
0233         return nullptr;
0234     }
0235 
0236     if (auto *memberExpr = dyn_cast<MemberExpr>(child2)) {
0237         return memberExpr->getMemberDecl();
0238     } else {
0239         std::vector<DeclRefExpr *> refs;
0240         clazy::getChilds<DeclRefExpr>(child2, refs);
0241         if (refs.size() == 1) {
0242             return refs[0]->getDecl();
0243         }
0244     }
0245 
0246     return nullptr;
0247 }
0248 
0249 clang::ValueDecl *Utils::valueDeclForCallExpr(clang::CallExpr *expr)
0250 {
0251     if (auto *memberExpr = dyn_cast<CXXMemberCallExpr>(expr)) {
0252         return valueDeclForMemberCall(memberExpr);
0253     }
0254     if (auto *operatorExpr = dyn_cast<CXXOperatorCallExpr>(expr)) {
0255         return valueDeclForOperatorCall(operatorExpr);
0256     }
0257 
0258     return nullptr;
0259 }
0260 
0261 static bool referencesVar(Stmt *s, const VarDecl *varDecl)
0262 {
0263     // look for a DeclRefExpr that references varDecl
0264     while (s) {
0265         auto it = s->child_begin();
0266         Stmt *child = it == s->child_end() ? nullptr : *it;
0267         if (auto *declRef = dyn_cast_or_null<DeclRefExpr>(child)) {
0268             if (declRef->getDecl() == varDecl) {
0269                 return true;
0270             }
0271         }
0272         s = child;
0273     }
0274 
0275     return false;
0276 }
0277 
0278 bool Utils::containsNonConstMemberCall(clang::ParentMap * /*map*/, Stmt *body, const VarDecl *varDecl)
0279 {
0280     if (!varDecl) {
0281         return false;
0282     }
0283 
0284     std::vector<CXXMemberCallExpr *> memberCallExprs;
0285     clazy::getChilds<CXXMemberCallExpr>(body, memberCallExprs);
0286     for (auto *memberCall : memberCallExprs) {
0287         CXXMethodDecl *methodDecl = memberCall->getMethodDecl();
0288         if (methodDecl && !methodDecl->isConst()) {
0289             ValueDecl *valueDecl = Utils::valueDeclForMemberCall(memberCall);
0290             if (valueDecl == varDecl) {
0291                 return true;
0292             }
0293         }
0294     }
0295 
0296     std::vector<CXXOperatorCallExpr *> operatorCalls;
0297     clazy::getChilds<CXXOperatorCallExpr>(body, operatorCalls);
0298     for (auto *operatorCall : operatorCalls) {
0299         FunctionDecl *fDecl = operatorCall->getDirectCallee();
0300         if (fDecl) {
0301             auto *methodDecl = dyn_cast<CXXMethodDecl>(fDecl);
0302             if (methodDecl && !methodDecl->isConst()) {
0303                 ValueDecl *valueDecl = Utils::valueDeclForOperatorCall(operatorCall);
0304                 if (valueDecl == varDecl) {
0305                     return true;
0306                 }
0307             }
0308         }
0309     }
0310 
0311     std::vector<BinaryOperator *> assignmentOperators;
0312     clazy::getChilds<BinaryOperator>(body, assignmentOperators);
0313     for (auto *op : assignmentOperators) {
0314         if (!op->isAssignmentOp()) {
0315             continue;
0316         }
0317 
0318         if (referencesVar(op, varDecl)) {
0319             return true;
0320         }
0321     }
0322 
0323     return false;
0324 }
0325 
0326 template<class T>
0327 static bool isArgOfFunc(T expr, FunctionDecl *fDecl, const VarDecl *varDecl, bool byRefOrPtrOnly)
0328 {
0329     unsigned int param = -1;
0330     for (auto arg : expr->arguments()) {
0331         ++param;
0332         auto refExpr = dyn_cast<DeclRefExpr>(arg);
0333         if (!refExpr) {
0334             if (clazy::hasChildren(arg)) {
0335                 Stmt *firstChild = *(arg->child_begin()); // Can be null (bug #362236)
0336                 refExpr = firstChild ? dyn_cast<DeclRefExpr>(firstChild) : nullptr;
0337                 if (!refExpr) {
0338                     continue;
0339                 }
0340             } else {
0341                 continue;
0342             }
0343         }
0344 
0345         if (refExpr->getDecl() != varDecl) { // It's our variable ?
0346             continue;
0347         }
0348 
0349         if (!byRefOrPtrOnly) {
0350             // We found it
0351             return true;
0352         }
0353 
0354         // It is, lets see if the callee takes our variable by const-ref
0355         if (param >= fDecl->param_size()) {
0356             continue;
0357         }
0358 
0359         ParmVarDecl *paramDecl = fDecl->getParamDecl(param);
0360         if (!paramDecl) {
0361             continue;
0362         }
0363 
0364         QualType qt = paramDecl->getType();
0365         const clang::Type *t = qt.getTypePtrOrNull();
0366         if (!t) {
0367             continue;
0368         }
0369 
0370         if ((t->isReferenceType() || t->isPointerType()) && !t->getPointeeType().isConstQualified()) {
0371             return true; // function receives non-const ref, so our foreach variable cant be const-ref
0372         }
0373     }
0374 
0375     return false;
0376 }
0377 
0378 bool Utils::isPassedToFunction(const StmtBodyRange &bodyRange, const VarDecl *varDecl, bool byRefOrPtrOnly)
0379 {
0380     if (!bodyRange.isValid()) {
0381         return false;
0382     }
0383 
0384     Stmt *body = bodyRange.body;
0385     std::vector<CallExpr *> callExprs;
0386     clazy::getChilds<CallExpr>(body, callExprs);
0387     for (CallExpr *callexpr : callExprs) {
0388         if (bodyRange.isOutsideRange(callexpr)) {
0389             continue;
0390         }
0391 
0392         FunctionDecl *fDecl = callexpr->getDirectCallee();
0393         if (!fDecl) {
0394             continue;
0395         }
0396 
0397         if (isArgOfFunc(callexpr, fDecl, varDecl, byRefOrPtrOnly)) {
0398             return true;
0399         }
0400     }
0401 
0402     std::vector<CXXConstructExpr *> constructExprs;
0403     clazy::getChilds<CXXConstructExpr>(body, constructExprs);
0404     for (CXXConstructExpr *constructExpr : constructExprs) {
0405         if (bodyRange.isOutsideRange(constructExpr)) {
0406             continue;
0407         }
0408         FunctionDecl *fDecl = constructExpr->getConstructor();
0409         if (isArgOfFunc(constructExpr, fDecl, varDecl, byRefOrPtrOnly)) {
0410             return true;
0411         }
0412     }
0413 
0414     return false;
0415 }
0416 
0417 bool Utils::addressIsTaken(const clang::CompilerInstance & /*ci*/, Stmt *body, const clang::ValueDecl *valDecl)
0418 {
0419     if (!body || !valDecl) {
0420         return false;
0421     }
0422 
0423     auto unaries = clazy::getStatements<UnaryOperator>(body);
0424     return clazy::any_of(unaries, [valDecl](UnaryOperator *op) {
0425         if (op->getOpcode() != clang::UO_AddrOf) {
0426             return false;
0427         }
0428 
0429         auto *declRef = clazy::getFirstChildOfType<DeclRefExpr>(op);
0430         return declRef && declRef->getDecl() == valDecl;
0431     });
0432 }
0433 
0434 bool Utils::isReturned(Stmt *body, const VarDecl *varDecl)
0435 {
0436     if (!body) {
0437         return false;
0438     }
0439 
0440     std::vector<ReturnStmt *> returns;
0441     clazy::getChilds<ReturnStmt>(body, returns);
0442     for (ReturnStmt *returnStmt : returns) {
0443         Expr *retValue = returnStmt->getRetValue();
0444         if (!retValue) {
0445             continue;
0446         }
0447         auto *declRef = clazy::unpeal<DeclRefExpr>(retValue, clazy::IgnoreImplicitCasts);
0448         if (!declRef) {
0449             continue;
0450         }
0451         if (declRef->getDecl() == varDecl) {
0452             return true;
0453         }
0454     }
0455 
0456     return false;
0457 }
0458 
0459 bool Utils::isAssignedTo(Stmt *body, const VarDecl *varDecl)
0460 {
0461     if (!body) {
0462         return false;
0463     }
0464 
0465     std::vector<BinaryOperator *> operatorCalls;
0466     clazy::getChilds<BinaryOperator>(body, operatorCalls);
0467     for (BinaryOperator *binaryOperator : operatorCalls) {
0468         if (binaryOperator->getOpcode() != clang::BO_Assign) {
0469             continue;
0470         }
0471 
0472         Expr *rhs = binaryOperator->getRHS();
0473         auto *declRef = clazy::unpeal<DeclRefExpr>(rhs, clazy::IgnoreImplicitCasts);
0474         if (!declRef) {
0475             continue;
0476         }
0477 
0478         if (declRef->getDecl() == varDecl) {
0479             return true;
0480         }
0481     }
0482 
0483     return false;
0484 }
0485 
0486 bool Utils::isAssignedFrom(Stmt *body, const VarDecl *varDecl)
0487 {
0488     if (!body) {
0489         return false;
0490     }
0491 
0492     std::vector<CXXOperatorCallExpr *> operatorCalls;
0493     clazy::getChilds<CXXOperatorCallExpr>(body, operatorCalls);
0494     for (CXXOperatorCallExpr *operatorExpr : operatorCalls) {
0495         FunctionDecl *fDecl = operatorExpr->getDirectCallee();
0496         if (!fDecl) {
0497             continue;
0498         }
0499 
0500         auto *methodDecl = dyn_cast<CXXMethodDecl>(fDecl);
0501         if (methodDecl && methodDecl->isCopyAssignmentOperator()) {
0502             ValueDecl *valueDecl = Utils::valueDeclForOperatorCall(operatorExpr);
0503             if (valueDecl == varDecl) {
0504                 return true;
0505             }
0506         }
0507     }
0508 
0509     return false;
0510 }
0511 
0512 bool Utils::callHasDefaultArguments(clang::CallExpr *expr)
0513 {
0514     std::vector<clang::CXXDefaultArgExpr *> exprs;
0515     clazy::getChilds<clang::CXXDefaultArgExpr>(expr, exprs, 1);
0516     return !exprs.empty();
0517 }
0518 
0519 bool Utils::containsStringLiteral(Stmt *stm, bool allowEmpty, int depth)
0520 {
0521     if (!stm) {
0522         return false;
0523     }
0524 
0525     std::vector<StringLiteral *> stringLiterals;
0526     clazy::getChilds<StringLiteral>(stm, stringLiterals, depth);
0527 
0528     if (allowEmpty) {
0529         return !stringLiterals.empty();
0530     }
0531 
0532     for (StringLiteral *sl : stringLiterals) {
0533         if (sl->getLength() > 0) {
0534             return true;
0535         }
0536     }
0537 
0538     return false;
0539 }
0540 
0541 bool Utils::ternaryOperatorIsOfStringLiteral(ConditionalOperator *ternary)
0542 {
0543     bool skipFirst = true;
0544     for (auto *child : ternary->children()) {
0545         if (skipFirst) {
0546             skipFirst = false;
0547             continue;
0548         }
0549 
0550         if (isa<StringLiteral>(child)) {
0551             continue;
0552         }
0553 
0554         auto *arrayToPointerDecay = dyn_cast<ImplicitCastExpr>(child);
0555         if (!arrayToPointerDecay || !isa<StringLiteral>(*(arrayToPointerDecay->child_begin()))) {
0556             return false;
0557         }
0558     }
0559 
0560     return true;
0561 }
0562 
0563 bool Utils::isAssignOperator(CXXOperatorCallExpr *op, StringRef className, StringRef argumentType, const clang::LangOptions &lo)
0564 {
0565     if (!op) {
0566         return false;
0567     }
0568 
0569     FunctionDecl *functionDecl = op->getDirectCallee();
0570     if (!functionDecl || functionDecl->param_size() != 1) {
0571         return false;
0572     }
0573 
0574     if (!className.empty()) {
0575         auto *methodDecl = dyn_cast<clang::CXXMethodDecl>(functionDecl);
0576         if (!clazy::isOfClass(methodDecl, className)) {
0577             return false;
0578         }
0579     }
0580 
0581     if (functionDecl->getNameAsString() != "operator=") {
0582         return false;
0583     }
0584 
0585     if (!argumentType.empty() && !clazy::hasArgumentOfType(functionDecl, argumentType, lo)) {
0586         return false;
0587     }
0588 
0589     return true;
0590 }
0591 
0592 bool Utils::isImplicitCastTo(Stmt *s, const std::string &className)
0593 {
0594     auto *expr = dyn_cast<ImplicitCastExpr>(s);
0595     if (!expr) {
0596         return false;
0597     }
0598 
0599     const auto *record = expr->getBestDynamicClassType();
0600     return record && clazy::name(record) == className;
0601 }
0602 
0603 bool Utils::isInsideOperatorCall(ParentMap *map, Stmt *s, const std::vector<StringRef> &anyOf)
0604 {
0605     if (!s) {
0606         return false;
0607     }
0608 
0609     auto *oper = dyn_cast<CXXOperatorCallExpr>(s);
0610     if (oper) {
0611         auto *func = oper->getDirectCallee();
0612         if (func) {
0613             if (anyOf.empty()) {
0614                 return true;
0615             }
0616 
0617             auto *method = dyn_cast<CXXMethodDecl>(func);
0618             if (method) {
0619                 auto *record = method->getParent();
0620                 if (record && clazy::contains(anyOf, clazy::name(record))) {
0621                     return true;
0622                 }
0623             }
0624         }
0625     }
0626 
0627     return isInsideOperatorCall(map, clazy::parent(map, s), anyOf);
0628 }
0629 
0630 bool Utils::insideCTORCall(ParentMap *map, Stmt *s, const std::vector<llvm::StringRef> &anyOf)
0631 {
0632     if (!s) {
0633         return false;
0634     }
0635 
0636     auto *expr = dyn_cast<CXXConstructExpr>(s);
0637     if (expr && expr->getConstructor() && clazy::contains(anyOf, clazy::name(expr->getConstructor()))) {
0638         return true;
0639     }
0640 
0641     return insideCTORCall(map, clazy::parent(map, s), anyOf);
0642 }
0643 
0644 bool Utils::presumedLocationsEqual(const clang::PresumedLoc &l1, const clang::PresumedLoc &l2)
0645 {
0646     return l1.isValid() && l2.isValid() && l1.getColumn() == l2.getColumn() && l1.getLine() == l2.getLine()
0647         && StringRef(l1.getFilename()) == StringRef(l2.getFilename());
0648 }
0649 
0650 CXXRecordDecl *Utils::isMemberVariable(ValueDecl *decl)
0651 {
0652     return decl ? dyn_cast<CXXRecordDecl>(decl->getDeclContext()) : nullptr;
0653 }
0654 
0655 std::vector<CXXMethodDecl *> Utils::methodsFromString(const CXXRecordDecl *record, const std::string &methodName)
0656 {
0657     if (!record) {
0658         return {};
0659     }
0660 
0661     std::vector<CXXMethodDecl *> methods;
0662     clazy::append_if(record->methods(), methods, [methodName](CXXMethodDecl *m) {
0663         return clazy::name(m) == methodName;
0664     });
0665 
0666     // Also include the base classes
0667     for (auto base : record->bases()) {
0668         const Type *t = base.getType().getTypePtrOrNull();
0669         if (t) {
0670             auto baseMethods = methodsFromString(t->getAsCXXRecordDecl(), methodName);
0671             if (!baseMethods.empty()) {
0672                 clazy::append(baseMethods, methods);
0673             }
0674         }
0675     }
0676 
0677     return methods;
0678 }
0679 
0680 const CXXRecordDecl *Utils::recordForMemberCall(CXXMemberCallExpr *call, std::string &implicitCallee)
0681 {
0682     implicitCallee.clear();
0683     Expr *implicitArgument = call->getImplicitObjectArgument();
0684     if (!implicitArgument) {
0685         return nullptr;
0686     }
0687 
0688     Stmt *s = implicitArgument;
0689     while (s) {
0690         if (auto *declRef = dyn_cast<DeclRefExpr>(s)) {
0691             if (declRef->getDecl()) {
0692                 implicitCallee = declRef->getDecl()->getNameAsString();
0693                 QualType qt = declRef->getDecl()->getType();
0694                 return qt->getPointeeCXXRecordDecl();
0695             }
0696             return nullptr;
0697 
0698         } else if (auto *thisExpr = dyn_cast<CXXThisExpr>(s)) {
0699             implicitCallee = "this";
0700             return thisExpr->getType()->getPointeeCXXRecordDecl();
0701         } else if (auto *memberExpr = dyn_cast<MemberExpr>(s)) {
0702             auto *decl = memberExpr->getMemberDecl();
0703             if (decl) {
0704                 implicitCallee = decl->getNameAsString();
0705                 QualType qt = decl->getType();
0706                 return qt->getPointeeCXXRecordDecl();
0707             }
0708             return nullptr;
0709         }
0710 
0711         s = s->child_begin() == s->child_end() ? nullptr : *(s->child_begin());
0712     }
0713 
0714     return nullptr;
0715 }
0716 
0717 bool Utils::isAscii(StringLiteral *lt)
0718 {
0719     // 'é' for some reason has isAscii() == true, so also call containsNonAsciiOrNull
0720     return lt && clazy::isAscii(lt) && !lt->containsNonAsciiOrNull();
0721 }
0722 
0723 bool Utils::isInDerefExpression(Stmt *s, ParentMap *map)
0724 {
0725     if (!s) {
0726         return false;
0727     }
0728 
0729     Stmt *p = s;
0730     do {
0731         p = clazy::parent(map, p);
0732         auto *op = p ? dyn_cast<CXXOperatorCallExpr>(p) : nullptr;
0733         if (op && op->getOperator() == OO_Star) {
0734             return op;
0735         }
0736     } while (p);
0737 
0738     return false;
0739 }
0740 
0741 std::vector<CallExpr *> Utils::callListForChain(CallExpr *lastCallExpr)
0742 {
0743     if (!lastCallExpr) {
0744         return {};
0745     }
0746 
0747     const bool isOperator = isa<CXXOperatorCallExpr>(lastCallExpr);
0748     std::vector<CallExpr *> callexprs = {lastCallExpr};
0749     Stmt *s = lastCallExpr;
0750     do {
0751         const int childCount = std::distance(s->child_begin(), s->child_end());
0752         if (isOperator && childCount > 1 && s == lastCallExpr) {
0753             // for operator case, the chained call childs are in the second child
0754             s = *(++s->child_begin());
0755         } else {
0756             s = childCount > 0 ? *s->child_begin() : nullptr;
0757         }
0758 
0759         if (s) {
0760             auto *callExpr = dyn_cast<CallExpr>(s);
0761             if (callExpr && callExpr->getCalleeDecl()) {
0762                 callexprs.push_back(callExpr);
0763             } else if (auto *memberExpr = dyn_cast<MemberExpr>(s)) {
0764                 if (isa<FieldDecl>(memberExpr->getMemberDecl())) {
0765                     break; // accessing a public member via . or -> breaks the chain
0766                 }
0767             } else if (isa<ConditionalOperator>(s)) {
0768                 // Gets very greasy with conditional operators
0769                 // This would match: (should() ? container1 : container2).append()
0770                 // and it would return { append(), should()}
0771                 break;
0772             }
0773         }
0774     } while (s);
0775 
0776     return callexprs;
0777 }
0778 
0779 CXXRecordDecl *Utils::rootBaseClass(CXXRecordDecl *derived)
0780 {
0781     if (!derived || derived->getNumBases() == 0) {
0782         return derived;
0783     }
0784 
0785     CXXBaseSpecifier *base = derived->bases_begin();
0786     CXXRecordDecl *record = base->getType()->getAsCXXRecordDecl();
0787 
0788     return record ? rootBaseClass(record) : derived;
0789 }
0790 
0791 CXXConstructorDecl *Utils::copyCtor(const CXXRecordDecl *record)
0792 {
0793     for (auto *ctor : record->ctors()) {
0794         if (ctor->isCopyConstructor()) {
0795             return ctor;
0796         }
0797     }
0798 
0799     return nullptr;
0800 }
0801 
0802 CXXMethodDecl *Utils::copyAssign(const CXXRecordDecl *record)
0803 {
0804     for (auto *copyAssign : record->methods()) {
0805         if (copyAssign->isCopyAssignmentOperator()) {
0806             return copyAssign;
0807         }
0808     }
0809 
0810     return nullptr;
0811 }
0812 
0813 bool Utils::hasMember(CXXRecordDecl *record, const std::string &memberTypeName)
0814 {
0815     if (!record) {
0816         return false;
0817     }
0818 
0819     for (auto *field : record->fields()) {
0820         field->getParent()->getNameAsString();
0821         QualType qt = field->getType();
0822         const Type *t = qt.getTypePtrOrNull();
0823         if (t && t->getAsCXXRecordDecl()) {
0824             CXXRecordDecl *rec = t->getAsCXXRecordDecl();
0825             if (clazy::name(rec) == memberTypeName) {
0826                 return true;
0827             }
0828         }
0829     }
0830 
0831     return false;
0832 }
0833 
0834 bool Utils::isSharedPointer(CXXRecordDecl *record)
0835 {
0836     static const std::vector<std::string> names = {"std::shared_ptr", "QSharedPointer", "boost::shared_ptr"};
0837     return record ? clazy::contains(names, record->getQualifiedNameAsString()) : false;
0838 }
0839 
0840 bool Utils::isInitializedExternally(clang::VarDecl *varDecl)
0841 {
0842     if (!varDecl) {
0843         return false;
0844     }
0845 
0846     DeclContext *context = varDecl->getDeclContext();
0847     auto *fDecl = context ? dyn_cast<FunctionDecl>(context) : nullptr;
0848     Stmt *body = fDecl ? fDecl->getBody() : nullptr;
0849     if (!body) {
0850         return false;
0851     }
0852 
0853     std::vector<DeclStmt *> declStmts;
0854     clazy::getChilds<DeclStmt>(body, declStmts);
0855     for (DeclStmt *declStmt : declStmts) {
0856         if (referencesVarDecl(declStmt, varDecl)) {
0857             std::vector<DeclRefExpr *> declRefs;
0858 
0859             clazy::getChilds<DeclRefExpr>(declStmt, declRefs);
0860             if (!declRefs.empty()) {
0861                 return true;
0862             }
0863 
0864             std::vector<CallExpr *> callExprs;
0865             clazy::getChilds<CallExpr>(declStmt, callExprs);
0866             if (!callExprs.empty()) {
0867                 return true;
0868             }
0869         }
0870     }
0871 
0872     return false;
0873 }
0874 
0875 bool Utils::functionHasEmptyBody(clang::FunctionDecl *func)
0876 {
0877     Stmt *body = func ? func->getBody() : nullptr;
0878     return !clazy::hasChildren(body);
0879 }
0880 
0881 clang::Expr *Utils::isWriteOperator(Stmt *stm)
0882 {
0883     if (!stm) {
0884         return nullptr;
0885     }
0886 
0887     if (auto *up = dyn_cast<UnaryOperator>(stm)) {
0888         auto opcode = up->getOpcode();
0889         if (opcode == clang::UO_AddrOf || opcode == clang::UO_Deref) {
0890             return nullptr;
0891         }
0892 
0893         return up->getSubExpr();
0894     }
0895 
0896     if (auto *bp = dyn_cast<BinaryOperator>(stm)) {
0897         return bp->getLHS();
0898     }
0899 
0900     return nullptr;
0901 }
0902 
0903 bool Utils::referencesVarDecl(clang::DeclStmt *declStmt, clang::VarDecl *varDecl)
0904 {
0905     if (!declStmt || !varDecl) {
0906         return false;
0907     }
0908 
0909     if (declStmt->isSingleDecl() && declStmt->getSingleDecl() == varDecl) {
0910         return true;
0911     }
0912 
0913     return clazy::any_of(declStmt->getDeclGroup(), [varDecl](Decl *decl) {
0914         return varDecl == decl;
0915     });
0916 }
0917 
0918 UserDefinedLiteral *Utils::userDefinedLiteral(Stmt *stm, const std::string &type, const clang::LangOptions &lo)
0919 {
0920     auto *udl = dyn_cast<UserDefinedLiteral>(stm);
0921     if (!udl) {
0922         udl = clazy::getFirstChildOfType<UserDefinedLiteral>(stm);
0923     }
0924 
0925     if (udl && clazy::returnTypeName(udl, lo) == type) {
0926         return udl;
0927     }
0928 
0929     return nullptr;
0930 }
0931 
0932 clang::ArrayRef<clang::ParmVarDecl *> Utils::functionParameters(clang::FunctionDecl *func)
0933 {
0934     return func->parameters();
0935 }
0936 
0937 std::vector<CXXCtorInitializer *> Utils::ctorInitializer(CXXConstructorDecl *ctor, clang::ParmVarDecl *param)
0938 {
0939     if (!ctor) {
0940         return {};
0941     }
0942 
0943     std::vector<CXXCtorInitializer *> result;
0944 
0945     for (auto it = ctor->init_begin(), end = ctor->init_end(); it != end; ++it) {
0946         auto *ctorInit = *it;
0947         std::vector<DeclRefExpr *> declRefs;
0948         clazy::getChilds(ctorInit->getInit(), declRefs);
0949         for (auto *declRef : declRefs) {
0950             if (declRef->getDecl() == param) {
0951                 result.push_back(ctorInit);
0952                 break;
0953             }
0954         }
0955     }
0956 
0957     return result;
0958 }
0959 
0960 bool Utils::ctorInitializerContainsMove(CXXCtorInitializer *init)
0961 {
0962     if (!init) {
0963         return false;
0964     }
0965 
0966     std::vector<CallExpr *> calls;
0967     clazy::getChilds(init->getInit(), calls);
0968 
0969     for (auto *call : calls) {
0970         if (FunctionDecl *funcDecl = call->getDirectCallee()) {
0971             auto name = funcDecl->getQualifiedNameAsString();
0972             if (name == "std::move" || name == "std::__1::move") {
0973                 return true;
0974             }
0975         }
0976     }
0977 
0978     return false;
0979 }
0980 
0981 bool Utils::ctorInitializerContainsMove(const std::vector<CXXCtorInitializer *> &ctorInits)
0982 {
0983     return clazy::any_of(ctorInits, [](CXXCtorInitializer *ctorInit) {
0984         return Utils::ctorInitializerContainsMove(ctorInit);
0985     });
0986 }
0987 
0988 std::string Utils::filenameForLoc(SourceLocation loc, const clang::SourceManager &sm)
0989 {
0990     if (loc.isMacroID()) {
0991         loc = sm.getExpansionLoc(loc);
0992     }
0993 
0994     const std::string filename = static_cast<std::string>(sm.getFilename(loc));
0995     auto splitted = clazy::splitString(filename, '/');
0996     if (splitted.empty()) {
0997         return {};
0998     }
0999 
1000     return splitted[splitted.size() - 1];
1001 }
1002 
1003 SourceLocation Utils::locForNextToken(SourceLocation loc, const clang::SourceManager &sm, const clang::LangOptions &lo)
1004 {
1005     std::pair<FileID, unsigned> locInfo = sm.getDecomposedLoc(loc);
1006     bool InvalidTemp = false;
1007     StringRef File = sm.getBufferData(locInfo.first, &InvalidTemp);
1008     if (InvalidTemp) {
1009         return {};
1010     }
1011 
1012     const char *TokenBegin = File.data() + locInfo.second;
1013     Lexer lexer(sm.getLocForStartOfFile(locInfo.first), lo, File.begin(), TokenBegin, File.end());
1014 
1015     Token Tok;
1016     lexer.LexFromRawLexer(Tok);
1017 
1018     SourceLocation TokenLoc = Tok.getLocation();
1019 
1020     // Calculate how much whitespace needs to be skipped if any.
1021     unsigned NumWhitespaceChars = 0;
1022     const char *TokenEnd = sm.getCharacterData(TokenLoc) + Tok.getLength();
1023     unsigned char C = *TokenEnd;
1024     while (isHorizontalWhitespace(C)) {
1025         C = *(++TokenEnd);
1026         NumWhitespaceChars++;
1027     }
1028 
1029     // Skip \r, \n, \r\n, or \n\r
1030     if (C == '\n' || C == '\r') {
1031         char PrevC = C;
1032         C = *(++TokenEnd);
1033         NumWhitespaceChars++;
1034         if ((C == '\n' || C == '\r') && C != PrevC) {
1035             NumWhitespaceChars++;
1036         }
1037     }
1038 
1039     return loc.getLocWithOffset(Tok.getLength() + NumWhitespaceChars);
1040 }
1041 
1042 bool Utils::literalContainsEscapedBytes(StringLiteral *lt, const SourceManager &sm, const LangOptions &lo)
1043 {
1044     if (!lt) {
1045         return false;
1046     }
1047 
1048     // The AST doesn't have the info, we need to ask the Lexer
1049     SourceRange sr = lt->getSourceRange();
1050     CharSourceRange cr = Lexer::getAsCharRange(sr, sm, lo);
1051     const StringRef str = Lexer::getSourceText(cr, sm, lo);
1052 
1053     for (int i = 0, size = str.size(); i < size - 1; ++i) {
1054         if (str[i] == '\\') {
1055             auto next = str[i + 1];
1056             if (next == 'U' || next == 'u' || next == 'x' || std::isdigit(next)) {
1057                 return true;
1058             }
1059         }
1060     }
1061 
1062     return false;
1063 }