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