File indexing completed on 2024-05-12 05:41:01
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 0005 SPDX-FileCopyrightText: 2015 Sergio Martins <smartins@kde.org> 0006 0007 SPDX-License-Identifier: LGPL-2.0-or-later 0008 */ 0009 0010 #include "virtual-call-ctor.h" 0011 #include "ClazyContext.h" 0012 #include "clazy_stl.h" 0013 0014 #include <clang/AST/DeclBase.h> 0015 #include <clang/AST/DeclCXX.h> 0016 #include <clang/AST/Expr.h> 0017 #include <clang/AST/ExprCXX.h> 0018 #include <clang/AST/Stmt.h> 0019 #include <clang/Basic/LLVM.h> 0020 #include <clang/Basic/SourceLocation.h> 0021 #include <llvm/Support/Casting.h> 0022 0023 class ClazyContext; 0024 0025 using namespace clang; 0026 0027 namespace clazy 0028 { 0029 void getChildsIgnoreLambda(clang::Stmt *stmt, std::vector<CXXMemberCallExpr *> &result_list, int depth = -1) 0030 { 0031 if (!stmt || dyn_cast<LambdaExpr>(stmt)) { 0032 return; 0033 } 0034 0035 auto *cexpr = llvm::dyn_cast<CXXMemberCallExpr>(stmt); 0036 if (cexpr) { 0037 result_list.push_back(cexpr); 0038 } 0039 0040 if (depth > 0 || depth == -1) { 0041 if (depth > 0) { 0042 --depth; 0043 } 0044 for (auto *child : stmt->children()) { 0045 getChildsIgnoreLambda(child, result_list, depth); 0046 } 0047 } 0048 } 0049 } 0050 0051 VirtualCallCtor::VirtualCallCtor(const std::string &name, ClazyContext *context) 0052 : CheckBase(name, context) 0053 { 0054 } 0055 0056 void VirtualCallCtor::VisitDecl(Decl *decl) 0057 { 0058 auto *ctorDecl = dyn_cast<CXXConstructorDecl>(decl); 0059 auto *dtorDecl = dyn_cast<CXXDestructorDecl>(decl); 0060 if (!ctorDecl && !dtorDecl) { 0061 return; 0062 } 0063 0064 Stmt *ctorOrDtorBody = ctorDecl ? ctorDecl->getBody() : dtorDecl->getBody(); 0065 if (!ctorOrDtorBody) { 0066 return; 0067 } 0068 0069 CXXRecordDecl *classDecl = ctorDecl ? ctorDecl->getParent() : dtorDecl->getParent(); 0070 0071 std::vector<Stmt *> processedStmts; 0072 SourceLocation loc = containsVirtualCall(classDecl, ctorOrDtorBody, processedStmts); 0073 if (loc.isValid()) { 0074 if (ctorDecl) { 0075 emitWarning(decl->getBeginLoc(), "Calling pure virtual function in CTOR"); 0076 } else { 0077 emitWarning(decl->getBeginLoc(), "Calling pure virtual function in DTOR"); 0078 } 0079 emitWarning(loc, "Called here"); 0080 } 0081 } 0082 0083 SourceLocation VirtualCallCtor::containsVirtualCall(clang::CXXRecordDecl *classDecl, clang::Stmt *stmt, std::vector<Stmt *> &processedStmts) 0084 { 0085 if (!stmt) { 0086 return {}; 0087 } 0088 0089 // already processed ? we don't want recurring calls 0090 if (clazy::contains(processedStmts, stmt)) { 0091 return {}; 0092 } 0093 0094 processedStmts.push_back(stmt); 0095 0096 std::vector<CXXMemberCallExpr *> memberCalls; 0097 0098 // Ignore lambdas, as they are usually used in connects. Might introduce true-negatives. Possible solution would be to check if the lambda is in a connect, 0099 // timer, invokeMethod, etc. 0100 clazy::getChildsIgnoreLambda(stmt, memberCalls); 0101 0102 for (CXXMemberCallExpr *callExpr : memberCalls) { 0103 CXXMethodDecl *memberDecl = callExpr->getMethodDecl(); 0104 if (!memberDecl || !isa<CXXThisExpr>(callExpr->getImplicitObjectArgument())) { 0105 continue; 0106 } 0107 0108 if (memberDecl->getParent() == classDecl) { 0109 #if LLVM_VERSION_MAJOR >= 18 0110 if (memberDecl->isPureVirtual()) { 0111 #else 0112 if (memberDecl->isPure()) { 0113 #endif 0114 return callExpr->getBeginLoc(); 0115 } 0116 if (containsVirtualCall(classDecl, memberDecl->getBody(), processedStmts).isValid()) { 0117 return callExpr->getBeginLoc(); 0118 } 0119 } 0120 } 0121 0122 return {}; 0123 }