src/Utils.cpp (860 lines of code) (raw):

/* SPDX-FileCopyrightText: 2015 Klarälvdalens Datakonsult AB a KDAB Group company info@kdab.com SPDX-FileContributor: Sérgio Martins <sergio.martins@kdab.com> SPDX-FileCopyrightText: 2015-2016 Sergio Martins <smartins@kde.org> SPDX-License-Identifier: LGPL-2.0-or-later */ #include "Utils.h" #include "HierarchyUtils.h" #include "SourceCompatibilityHelpers.h" #include "StmtBodyRange.h" #include "StringUtils.h" #include "clazy_stl.h" #include <clang/AST/Decl.h> #include <clang/AST/DeclBase.h> #include <clang/AST/DeclCXX.h> #include <clang/AST/DeclGroup.h> #include <clang/AST/DeclTemplate.h> #include <clang/AST/Expr.h> #include <clang/AST/ExprCXX.h> #include <clang/AST/OperationKinds.h> #include <clang/AST/Stmt.h> #include <clang/AST/StmtIterator.h> #include <clang/AST/Type.h> #include <clang/Basic/CharInfo.h> #include <clang/Basic/IdentifierTable.h> #include <clang/Basic/LLVM.h> #include <clang/Basic/SourceLocation.h> #include <clang/Basic/SourceManager.h> #include <clang/Lex/Lexer.h> #include <clang/Lex/Token.h> #include <llvm/Support/Casting.h> #include <cctype> #include <iterator> #include <utility> namespace clang { class LangOptions; } // namespace clang using namespace clang; bool Utils::hasConstexprCtor(CXXRecordDecl *decl) { return clazy::any_of(decl->ctors(), [](CXXConstructorDecl *ctor) { return ctor->isConstexpr(); }); } CXXRecordDecl *Utils::namedCastInnerDecl(CXXNamedCastExpr *staticOrDynamicCast) { Expr *e = staticOrDynamicCast->getSubExpr(); if (!e) { return nullptr; } if (auto *implicitCast = dyn_cast<ImplicitCastExpr>(e)) { // Sometimes it's automatically cast to base if (implicitCast->getCastKind() == CK_DerivedToBase) { e = implicitCast->getSubExpr(); } } QualType qt = e->getType(); const Type *t = qt.getTypePtrOrNull(); if (!t) { return nullptr; } QualType qt2 = t->getPointeeType(); const Type *t2 = qt2.getTypePtrOrNull(); if (!t2) { return nullptr; } return t2->getAsCXXRecordDecl(); } CXXRecordDecl *Utils::namedCastOuterDecl(CXXNamedCastExpr *staticOrDynamicCast) { QualType qt = staticOrDynamicCast->getTypeAsWritten(); const Type *t = qt.getTypePtrOrNull(); QualType qt2 = t->getPointeeType(); const Type *t2 = qt2.getTypePtrOrNull(); if (!t2) { return nullptr; } return t2->getAsCXXRecordDecl(); } bool Utils::allChildrenMemberCallsConst(Stmt *stm) { if (!stm) { return false; } auto *expr = dyn_cast<MemberExpr>(stm); if (expr) { auto *methodDecl = dyn_cast<CXXMethodDecl>(expr->getMemberDecl()); if (methodDecl && !methodDecl->isConst()) { return false; } } return clazy::all_of(stm->children(), [](Stmt *child) { return allChildrenMemberCallsConst(child); }); } bool Utils::childsHaveSideEffects(Stmt *stm) { if (!stm) { return false; } auto *unary = dyn_cast<UnaryOperator>(stm); if (unary && (unary->isIncrementOp() || unary->isDecrementOp())) { return true; } auto *binary = dyn_cast<BinaryOperator>(stm); if (binary && (binary->isAssignmentOp() || binary->isShiftAssignOp() || binary->isCompoundAssignmentOp())) { return true; } static const std::vector<StringRef> method_blacklist = { "isDestroyed", "isRecursive", // TODO: Use qualified name instead ? "q_func", "d_func", "begin", "end", "data", "fragment", "glIsRenderbuffer", }; auto *memberCall = dyn_cast<MemberExpr>(stm); if (memberCall) { auto *methodDecl = dyn_cast<CXXMethodDecl>(memberCall->getMemberDecl()); if (methodDecl && !methodDecl->isConst() && !methodDecl->isStatic() && !clazy::contains(method_blacklist, clazy::name(methodDecl))) { return true; } } /* // too many false positives, qIsFinite() etc for example auto callExpr = dyn_cast<CallExpr>(stm); if (callExpr) { FunctionDecl *callee = callExpr->getDirectCallee(); if (callee && callee->isGlobal()) return true; }*/ return clazy::any_of(stm->children(), [](Stmt *s) { return childsHaveSideEffects(s); }); } CXXRecordDecl *Utils::recordFromVarDecl(Decl *decl) { auto *varDecl = dyn_cast<VarDecl>(decl); if (!varDecl) { return nullptr; } QualType qt = varDecl->getType(); const Type *t = qt.getTypePtrOrNull(); if (!t) { return nullptr; } return t->getAsCXXRecordDecl(); } ClassTemplateSpecializationDecl *Utils::templateSpecializationFromVarDecl(Decl *decl) { auto *record = recordFromVarDecl(decl); if (record) { return dyn_cast<ClassTemplateSpecializationDecl>(record); } return nullptr; } ValueDecl *Utils::valueDeclForMemberCall(CXXMemberCallExpr *memberCall) { if (!memberCall) { return nullptr; } Expr *implicitObject = memberCall->getImplicitObjectArgument(); if (!implicitObject) { return nullptr; } auto *declRefExpr = dyn_cast<DeclRefExpr>(implicitObject); auto *memberExpr = dyn_cast<MemberExpr>(implicitObject); if (declRefExpr) { return declRefExpr->getDecl(); } if (memberExpr) { return memberExpr->getMemberDecl(); } // Maybe there's an implicit cast in between.. auto memberExprs = clazy::getStatements<MemberExpr>(implicitObject, nullptr, {}, /**depth=*/1, /*includeParent=*/true); auto declRefs = clazy::getStatements<DeclRefExpr>(implicitObject, nullptr, {}, /**depth=*/1, /*includeParent=*/true); if (!memberExprs.empty()) { return memberExprs.at(0)->getMemberDecl(); } if (!declRefs.empty()) { return declRefs.at(0)->getDecl(); } return nullptr; } ValueDecl *Utils::valueDeclForOperatorCall(CXXOperatorCallExpr *operatorCall) { if (!operatorCall) { return nullptr; } // CXXOperatorCallExpr doesn't have API to access the value decl. // By inspecting several ASTs I noticed it's always in the 2nd child Stmt *child2 = clazy::childAt(operatorCall, 1); if (!child2) { return nullptr; } if (auto *memberExpr = dyn_cast<MemberExpr>(child2)) { return memberExpr->getMemberDecl(); } else { std::vector<DeclRefExpr *> refs; clazy::getChilds<DeclRefExpr>(child2, refs); if (refs.size() == 1) { return refs[0]->getDecl(); } } return nullptr; } clang::ValueDecl *Utils::valueDeclForCallExpr(clang::CallExpr *expr) { if (auto *memberExpr = dyn_cast<CXXMemberCallExpr>(expr)) { return valueDeclForMemberCall(memberExpr); } if (auto *operatorExpr = dyn_cast<CXXOperatorCallExpr>(expr)) { return valueDeclForOperatorCall(operatorExpr); } return nullptr; } static bool referencesVar(Stmt *s, const VarDecl *varDecl) { // look for a DeclRefExpr that references varDecl while (s) { auto it = s->child_begin(); Stmt *child = it == s->child_end() ? nullptr : *it; if (auto *declRef = dyn_cast_or_null<DeclRefExpr>(child)) { if (declRef->getDecl() == varDecl) { return true; } } s = child; } return false; } bool Utils::containsNonConstMemberCall(clang::ParentMap * /*map*/, Stmt *body, const VarDecl *varDecl) { if (!varDecl) { return false; } std::vector<CXXMemberCallExpr *> memberCallExprs; clazy::getChilds<CXXMemberCallExpr>(body, memberCallExprs); for (auto *memberCall : memberCallExprs) { CXXMethodDecl *methodDecl = memberCall->getMethodDecl(); if (methodDecl && !methodDecl->isConst()) { const ValueDecl *valueDecl = Utils::valueDeclForMemberCall(memberCall); if (valueDecl == varDecl) { return true; } } } std::vector<CXXOperatorCallExpr *> operatorCalls; clazy::getChilds<CXXOperatorCallExpr>(body, operatorCalls); for (auto *operatorCall : operatorCalls) { FunctionDecl *fDecl = operatorCall->getDirectCallee(); if (fDecl) { auto *methodDecl = dyn_cast<CXXMethodDecl>(fDecl); if (methodDecl && !methodDecl->isConst()) { const ValueDecl *valueDecl = Utils::valueDeclForOperatorCall(operatorCall); if (valueDecl == varDecl) { return true; } } } } std::vector<BinaryOperator *> assignmentOperators; clazy::getChilds<BinaryOperator>(body, assignmentOperators); for (auto *op : assignmentOperators) { if (!op->isAssignmentOp()) { continue; } if (referencesVar(op, varDecl)) { return true; } } return false; } template<class T> static bool isArgOfFunc(T expr, FunctionDecl *fDecl, const VarDecl *varDecl, bool byRefOrPtrOnly) { unsigned int param = -1; for (auto arg : expr->arguments()) { ++param; auto refExpr = dyn_cast<DeclRefExpr>(arg); if (!refExpr) { if (clazy::hasChildren(arg)) { Stmt *firstChild = *(arg->child_begin()); // Can be null (bug #362236) refExpr = firstChild ? dyn_cast<DeclRefExpr>(firstChild) : nullptr; if (!refExpr) { continue; } } else { continue; } } if (refExpr->getDecl() != varDecl) { // It's our variable ? continue; } if (!byRefOrPtrOnly) { // We found it return true; } // It is, lets see if the callee takes our variable by const-ref if (param >= fDecl->param_size()) { continue; } ParmVarDecl *paramDecl = fDecl->getParamDecl(param); if (!paramDecl) { continue; } QualType qt = paramDecl->getType(); const clang::Type *t = qt.getTypePtrOrNull(); if (!t) { continue; } if ((t->isReferenceType() || t->isPointerType()) && !t->getPointeeType().isConstQualified()) { return true; // function receives non-const ref, so our foreach variable cant be const-ref } } return false; } bool Utils::isPassedToFunction(const StmtBodyRange &bodyRange, const VarDecl *varDecl, bool byRefOrPtrOnly) { if (!bodyRange.isValid()) { return false; } Stmt *body = bodyRange.body; std::vector<CallExpr *> callExprs; clazy::getChilds<CallExpr>(body, callExprs); for (CallExpr *callexpr : callExprs) { if (bodyRange.isOutsideRange(callexpr)) { continue; } FunctionDecl *fDecl = callexpr->getDirectCallee(); if (!fDecl) { continue; } if (isArgOfFunc(callexpr, fDecl, varDecl, byRefOrPtrOnly)) { return true; } } std::vector<CXXConstructExpr *> constructExprs; clazy::getChilds<CXXConstructExpr>(body, constructExprs); for (CXXConstructExpr *constructExpr : constructExprs) { if (bodyRange.isOutsideRange(constructExpr)) { continue; } FunctionDecl *fDecl = constructExpr->getConstructor(); if (isArgOfFunc(constructExpr, fDecl, varDecl, byRefOrPtrOnly)) { return true; } } return false; } bool Utils::addressIsTaken(const clang::CompilerInstance & /*ci*/, Stmt *body, const clang::ValueDecl *valDecl) { if (!body || !valDecl) { return false; } auto unaries = clazy::getStatements<UnaryOperator>(body); return clazy::any_of(unaries, [valDecl](UnaryOperator *op) { if (op->getOpcode() != clang::UO_AddrOf) { return false; } auto *declRef = clazy::getFirstChildOfType<DeclRefExpr>(op); return declRef && declRef->getDecl() == valDecl; }); } bool Utils::isReturned(Stmt *body, const VarDecl *varDecl) { if (!body) { return false; } std::vector<ReturnStmt *> returns; clazy::getChilds<ReturnStmt>(body, returns); for (ReturnStmt *returnStmt : returns) { Expr *retValue = returnStmt->getRetValue(); if (!retValue) { continue; } auto *declRef = clazy::unpeal<DeclRefExpr>(retValue, clazy::IgnoreImplicitCasts); if (!declRef) { continue; } if (declRef->getDecl() == varDecl) { return true; } } return false; } bool Utils::isAssignedTo(Stmt *body, const VarDecl *varDecl) { if (!body) { return false; } std::vector<BinaryOperator *> operatorCalls; clazy::getChilds<BinaryOperator>(body, operatorCalls); for (BinaryOperator *binaryOperator : operatorCalls) { if (binaryOperator->getOpcode() != clang::BO_Assign) { continue; } Expr *rhs = binaryOperator->getRHS(); auto *declRef = clazy::unpeal<DeclRefExpr>(rhs, clazy::IgnoreImplicitCasts); if (!declRef) { continue; } if (declRef->getDecl() == varDecl) { return true; } } return false; } bool Utils::isAssignedFrom(Stmt *body, const VarDecl *varDecl) { if (!body) { return false; } std::vector<CXXOperatorCallExpr *> operatorCalls; clazy::getChilds<CXXOperatorCallExpr>(body, operatorCalls); for (CXXOperatorCallExpr *operatorExpr : operatorCalls) { FunctionDecl *fDecl = operatorExpr->getDirectCallee(); if (!fDecl) { continue; } auto *methodDecl = dyn_cast<CXXMethodDecl>(fDecl); if (methodDecl && methodDecl->isCopyAssignmentOperator()) { const ValueDecl *valueDecl = Utils::valueDeclForOperatorCall(operatorExpr); if (valueDecl == varDecl) { return true; } } } return false; } bool Utils::callHasDefaultArguments(clang::CallExpr *expr) { std::vector<clang::CXXDefaultArgExpr *> exprs; clazy::getChilds<clang::CXXDefaultArgExpr>(expr, exprs, 1); return !exprs.empty(); } bool Utils::containsStringLiteral(Stmt *stm, bool allowEmpty, int depth) { if (!stm) { return false; } std::vector<StringLiteral *> stringLiterals; clazy::getChilds<StringLiteral>(stm, stringLiterals, depth); if (allowEmpty) { return !stringLiterals.empty(); } for (StringLiteral *sl : stringLiterals) { if (sl->getLength() > 0) { return true; } } return false; } bool Utils::ternaryOperatorIsOfStringLiteral(ConditionalOperator *ternary) { bool skipFirst = true; for (auto *child : ternary->children()) { if (skipFirst) { skipFirst = false; continue; } if (isa<StringLiteral>(child)) { continue; } auto *arrayToPointerDecay = dyn_cast<ImplicitCastExpr>(child); if (!arrayToPointerDecay || !isa<StringLiteral>(*(arrayToPointerDecay->child_begin()))) { return false; } } return true; } bool Utils::isAssignOperator(CXXOperatorCallExpr *op, StringRef className, StringRef argumentType, const clang::LangOptions &lo) { if (!op) { return false; } FunctionDecl *functionDecl = op->getDirectCallee(); if (!functionDecl || functionDecl->param_size() != 1) { return false; } if (!className.empty()) { auto *methodDecl = dyn_cast<clang::CXXMethodDecl>(functionDecl); if (!clazy::isOfClass(methodDecl, className)) { return false; } } if (functionDecl->getOverloadedOperator() != clang::OO_Equal) { return false; } if (!argumentType.empty() && !clazy::hasArgumentOfType(functionDecl, argumentType, lo)) { return false; } return true; } bool Utils::isImplicitCastTo(Stmt *s, const std::string &className) { auto *expr = dyn_cast<ImplicitCastExpr>(s); if (!expr) { return false; } const auto *record = expr->getBestDynamicClassType(); return record && clazy::name(record) == className; } bool Utils::isInsideOperatorCall(ParentMap *map, Stmt *s, const std::vector<StringRef> &anyOf) { if (!s) { return false; } auto *oper = dyn_cast<CXXOperatorCallExpr>(s); if (oper) { auto *func = oper->getDirectCallee(); if (func) { if (anyOf.empty()) { return true; } auto *method = dyn_cast<CXXMethodDecl>(func); if (method) { auto *record = method->getParent(); if (record && clazy::contains(anyOf, clazy::name(record))) { return true; } } } } return isInsideOperatorCall(map, clazy::parent(map, s), anyOf); } bool Utils::insideCTORCall(ParentMap *map, Stmt *s, const std::vector<llvm::StringRef> &anyOf) { if (!s) { return false; } auto *expr = dyn_cast<CXXConstructExpr>(s); if (expr && expr->getConstructor() && clazy::contains(anyOf, clazy::name(expr->getConstructor()))) { return true; } return insideCTORCall(map, clazy::parent(map, s), anyOf); } bool Utils::presumedLocationsEqual(const clang::PresumedLoc &l1, const clang::PresumedLoc &l2) { return l1.isValid() && l2.isValid() && l1.getColumn() == l2.getColumn() && l1.getLine() == l2.getLine() && StringRef(l1.getFilename()) == StringRef(l2.getFilename()); } CXXRecordDecl *Utils::isMemberVariable(ValueDecl *decl) { return decl ? dyn_cast<CXXRecordDecl>(decl->getDeclContext()) : nullptr; } std::vector<CXXMethodDecl *> Utils::methodsFromString(const CXXRecordDecl *record, const std::string &methodName) { if (!record) { return {}; } std::vector<CXXMethodDecl *> methods; clazy::append_if(record->methods(), methods, [methodName](CXXMethodDecl *m) { return clazy::name(m) == methodName; }); // Also include the base classes for (auto base : record->bases()) { const Type *t = base.getType().getTypePtrOrNull(); if (t) { auto baseMethods = methodsFromString(t->getAsCXXRecordDecl(), methodName); if (!baseMethods.empty()) { clazy::append(baseMethods, methods); } } } return methods; } const CXXRecordDecl *Utils::recordForMemberCall(CXXMemberCallExpr *call, std::string &implicitCallee) { implicitCallee.clear(); Expr *implicitArgument = call->getImplicitObjectArgument(); if (!implicitArgument) { return nullptr; } Stmt *s = implicitArgument; while (s) { if (auto *declRef = dyn_cast<DeclRefExpr>(s)) { if (declRef->getDecl()) { implicitCallee = declRef->getDecl()->getNameAsString(); QualType qt = declRef->getDecl()->getType(); return qt->getPointeeCXXRecordDecl(); } return nullptr; } else if (auto *thisExpr = dyn_cast<CXXThisExpr>(s)) { implicitCallee = "this"; return thisExpr->getType()->getPointeeCXXRecordDecl(); } else if (auto *memberExpr = dyn_cast<MemberExpr>(s)) { auto *decl = memberExpr->getMemberDecl(); if (decl) { implicitCallee = decl->getNameAsString(); QualType qt = decl->getType(); return qt->getPointeeCXXRecordDecl(); } return nullptr; } s = s->child_begin() == s->child_end() ? nullptr : *(s->child_begin()); } return nullptr; } bool Utils::isAscii(StringLiteral *lt) { // 'é' for some reason has isAscii() == true, so also call containsNonAsciiOrNull return lt && clazy::isAscii(lt) && !lt->containsNonAsciiOrNull(); } bool Utils::isInDerefExpression(Stmt *s, ParentMap *map) { if (!s) { return false; } Stmt *p = s; do { p = clazy::parent(map, p); auto *op = p ? dyn_cast<CXXOperatorCallExpr>(p) : nullptr; if (op && op->getOperator() == OO_Star) { return op; } } while (p); return false; } std::vector<CallExpr *> Utils::callListForChain(CallExpr *lastCallExpr) { if (!lastCallExpr) { return {}; } const bool isOperator = isa<CXXOperatorCallExpr>(lastCallExpr); std::vector<CallExpr *> callexprs = {lastCallExpr}; Stmt *s = lastCallExpr; do { const int childCount = std::distance(s->child_begin(), s->child_end()); if (isOperator && childCount > 1 && s == lastCallExpr) { // for operator case, the chained call childs are in the second child s = *(++s->child_begin()); } else { s = childCount > 0 ? *s->child_begin() : nullptr; } if (s) { auto *callExpr = dyn_cast<CallExpr>(s); if (callExpr && callExpr->getCalleeDecl()) { callexprs.push_back(callExpr); } else if (auto *memberExpr = dyn_cast<MemberExpr>(s)) { if (isa<FieldDecl>(memberExpr->getMemberDecl())) { break; // accessing a public member via . or -> breaks the chain } } else if (isa<ConditionalOperator>(s)) { // Gets very greasy with conditional operators // This would match: (should() ? container1 : container2).append() // and it would return { append(), should()} break; } } } while (s); return callexprs; } CXXRecordDecl *Utils::rootBaseClass(CXXRecordDecl *derived) { if (!derived || derived->getNumBases() == 0) { return derived; } CXXBaseSpecifier *base = derived->bases_begin(); CXXRecordDecl *record = base->getType()->getAsCXXRecordDecl(); return record ? rootBaseClass(record) : derived; } CXXConstructorDecl *Utils::copyCtor(const CXXRecordDecl *record) { for (auto *ctor : record->ctors()) { if (ctor->isCopyConstructor()) { return ctor; } } return nullptr; } CXXMethodDecl *Utils::copyAssign(const CXXRecordDecl *record) { for (auto *copyAssign : record->methods()) { if (copyAssign->isCopyAssignmentOperator()) { return copyAssign; } } return nullptr; } bool Utils::hasMember(CXXRecordDecl *record, const std::string &memberTypeName) { if (!record) { return false; } for (auto *field : record->fields()) { field->getParent()->getNameAsString(); QualType qt = field->getType(); const Type *t = qt.getTypePtrOrNull(); if (t && t->getAsCXXRecordDecl()) { const CXXRecordDecl *rec = t->getAsCXXRecordDecl(); if (clazy::name(rec) == memberTypeName) { return true; } } } return false; } bool Utils::isSharedPointer(CXXRecordDecl *record) { static const std::vector<std::string> names = {"std::shared_ptr", "QSharedPointer", "boost::shared_ptr"}; return record ? clazy::contains(names, record->getQualifiedNameAsString()) : false; } bool Utils::isInitializedExternally(clang::VarDecl *varDecl) { if (!varDecl) { return false; } DeclContext *context = varDecl->getDeclContext(); auto *fDecl = context ? dyn_cast<FunctionDecl>(context) : nullptr; Stmt *body = fDecl ? fDecl->getBody() : nullptr; if (!body) { return false; } std::vector<DeclStmt *> declStmts; clazy::getChilds<DeclStmt>(body, declStmts); for (DeclStmt *declStmt : declStmts) { if (referencesVarDecl(declStmt, varDecl)) { std::vector<DeclRefExpr *> declRefs; clazy::getChilds<DeclRefExpr>(declStmt, declRefs); if (!declRefs.empty()) { return true; } std::vector<CallExpr *> callExprs; clazy::getChilds<CallExpr>(declStmt, callExprs); if (!callExprs.empty()) { return true; } } } return false; } bool Utils::functionHasEmptyBody(clang::FunctionDecl *func) { Stmt *body = func ? func->getBody() : nullptr; return !clazy::hasChildren(body); } clang::Expr *Utils::isWriteOperator(Stmt *stm) { if (!stm) { return nullptr; } if (auto *up = dyn_cast<UnaryOperator>(stm)) { auto opcode = up->getOpcode(); if (opcode == clang::UO_AddrOf || opcode == clang::UO_Deref) { return nullptr; } return up->getSubExpr(); } if (auto *bp = dyn_cast<BinaryOperator>(stm)) { return bp->getLHS(); } return nullptr; } bool Utils::referencesVarDecl(clang::DeclStmt *declStmt, clang::VarDecl *varDecl) { if (!declStmt || !varDecl) { return false; } if (declStmt->isSingleDecl() && declStmt->getSingleDecl() == varDecl) { return true; } return clazy::any_of(declStmt->getDeclGroup(), [varDecl](Decl *decl) { return varDecl == decl; }); } UserDefinedLiteral *Utils::userDefinedLiteral(Stmt *stm, const std::string &type, const clang::LangOptions &lo) { auto *udl = dyn_cast<UserDefinedLiteral>(stm); if (!udl) { udl = clazy::getFirstChildOfType<UserDefinedLiteral>(stm); } if (udl && clazy::returnTypeName(udl, lo) == type) { return udl; } return nullptr; } clang::ArrayRef<clang::ParmVarDecl *> Utils::functionParameters(clang::FunctionDecl *func) { return func->parameters(); } std::vector<CXXCtorInitializer *> Utils::ctorInitializer(CXXConstructorDecl *ctor, clang::ParmVarDecl *param) { if (!ctor) { return {}; } std::vector<CXXCtorInitializer *> result; for (auto it = ctor->init_begin(), end = ctor->init_end(); it != end; ++it) { auto *ctorInit = *it; std::vector<DeclRefExpr *> declRefs; clazy::getChilds(ctorInit->getInit(), declRefs); for (auto *declRef : declRefs) { if (declRef->getDecl() == param) { result.push_back(ctorInit); break; } } } return result; } bool Utils::ctorInitializerContainsMove(CXXCtorInitializer *init) { if (!init) { return false; } std::vector<CallExpr *> calls; clazy::getChilds(init->getInit(), calls); for (auto *call : calls) { if (FunctionDecl *funcDecl = call->getDirectCallee()) { auto name = funcDecl->getQualifiedNameAsString(); if (name == "std::move" || name == "std::__1::move") { return true; } } } return false; } bool Utils::ctorInitializerContainsMove(const std::vector<CXXCtorInitializer *> &ctorInits) { return clazy::any_of(ctorInits, [](CXXCtorInitializer *ctorInit) { return Utils::ctorInitializerContainsMove(ctorInit); }); } std::string Utils::filenameForLoc(SourceLocation loc, const clang::SourceManager &sm) { if (loc.isMacroID()) { loc = sm.getExpansionLoc(loc); } const std::string filename = static_cast<std::string>(sm.getFilename(loc)); auto splitted = clazy::splitString(filename, '/'); if (splitted.empty()) { return {}; } return splitted[splitted.size() - 1]; } SourceLocation Utils::locForNextToken(SourceLocation loc, const clang::SourceManager &sm, const clang::LangOptions &lo) { std::pair<FileID, unsigned> locInfo = sm.getDecomposedLoc(loc); bool InvalidTemp = false; StringRef File = sm.getBufferData(locInfo.first, &InvalidTemp); if (InvalidTemp) { return {}; } const char *TokenBegin = File.data() + locInfo.second; Lexer lexer(sm.getLocForStartOfFile(locInfo.first), lo, File.begin(), TokenBegin, File.end()); Token Tok; lexer.LexFromRawLexer(Tok); SourceLocation TokenLoc = Tok.getLocation(); // Calculate how much whitespace needs to be skipped if any. unsigned NumWhitespaceChars = 0; const char *TokenEnd = sm.getCharacterData(TokenLoc) + Tok.getLength(); unsigned char C = *TokenEnd; while (isHorizontalWhitespace(C)) { C = *(++TokenEnd); NumWhitespaceChars++; } // Skip \r, \n, \r\n, or \n\r if (C == '\n' || C == '\r') { char PrevC = C; C = *(++TokenEnd); NumWhitespaceChars++; if ((C == '\n' || C == '\r') && C != PrevC) { NumWhitespaceChars++; } } return loc.getLocWithOffset(Tok.getLength() + NumWhitespaceChars); } bool Utils::literalContainsEscapedBytes(StringLiteral *lt, const SourceManager &sm, const LangOptions &lo) { if (!lt) { return false; } // The AST doesn't have the info, we need to ask the Lexer SourceRange sr = lt->getSourceRange(); CharSourceRange cr = Lexer::getAsCharRange(sr, sm, lo); const StringRef str = Lexer::getSourceText(cr, sm, lo); for (int i = 0, size = str.size(); i < size - 1; ++i) { if (str[i] == '\\') { auto next = str[i + 1]; if (next == 'U' || next == 'u' || next == 'x' || std::isdigit(next)) { return true; } } } return false; }