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 }