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 }