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;
}