File indexing completed on 2024-05-12 05:40:59

0001 /*
0002     SPDX-FileCopyrightText: 2016-2017 Sergio Martins <smartins@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "returning-data-from-temporary.h"
0008 #include "HierarchyUtils.h"
0009 #include "TypeUtils.h"
0010 
0011 #include <clang/AST/Decl.h>
0012 #include <clang/AST/DeclCXX.h>
0013 #include <clang/AST/Expr.h>
0014 #include <clang/AST/ExprCXX.h>
0015 #include <clang/AST/Stmt.h>
0016 #include <clang/AST/Type.h>
0017 #include <clang/Basic/LLVM.h>
0018 #include <llvm/Support/Casting.h>
0019 
0020 class ClazyContext;
0021 
0022 using namespace clang;
0023 
0024 ReturningDataFromTemporary::ReturningDataFromTemporary(const std::string &name, ClazyContext *context)
0025     : CheckBase(name, context, Option_CanIgnoreIncludes)
0026 {
0027 }
0028 
0029 void ReturningDataFromTemporary::VisitStmt(clang::Stmt *stmt)
0030 {
0031     if (handleReturn(dyn_cast<ReturnStmt>(stmt))) {
0032         return;
0033     }
0034 
0035     handleDeclStmt(dyn_cast<DeclStmt>(stmt));
0036 }
0037 
0038 bool ReturningDataFromTemporary::handleReturn(ReturnStmt *ret)
0039 {
0040     if (!ret) {
0041         return false;
0042     }
0043 
0044     auto *memberCall = clazy::unpeal<CXXMemberCallExpr>(clazy::getFirstChild(ret), clazy::IgnoreExprWithCleanups | clazy::IgnoreImplicitCasts);
0045     handleMemberCall(memberCall, false);
0046     return true;
0047 }
0048 
0049 void ReturningDataFromTemporary::handleDeclStmt(DeclStmt *declStmt)
0050 {
0051     if (!declStmt) {
0052         return;
0053     }
0054 
0055     for (auto *decl : declStmt->decls()) {
0056         auto *varDecl = dyn_cast<VarDecl>(decl);
0057         if (!varDecl) {
0058             continue;
0059         }
0060 
0061         if (varDecl->getType().getAsString() != "const char *") {
0062             continue;
0063         }
0064 
0065         Expr *init = varDecl->getInit();
0066         if (!init) {
0067             continue;
0068         }
0069 
0070         auto *memberCall = clazy::unpeal<CXXMemberCallExpr>(clazy::getFirstChild(init), clazy::IgnoreExprWithCleanups | clazy::IgnoreImplicitCasts);
0071 
0072         handleMemberCall(memberCall, true);
0073     }
0074 }
0075 
0076 void ReturningDataFromTemporary::handleMemberCall(CXXMemberCallExpr *memberCall, bool onlyTemporaries)
0077 {
0078     if (!memberCall) {
0079         return;
0080     }
0081 
0082     CXXMethodDecl *method = memberCall->getMethodDecl();
0083     if (!method) {
0084         return;
0085     }
0086     const auto methodName = method->getQualifiedNameAsString();
0087 
0088     if (methodName != "QByteArray::data" && methodName != "QByteArray::operator const char *" && methodName != "QByteArray::constData") {
0089         return;
0090     }
0091 
0092     Expr *obj = memberCall->getImplicitObjectArgument();
0093     Stmt *t = obj;
0094     DeclRefExpr *declRef = nullptr;
0095     CXXBindTemporaryExpr *temporaryExpr = nullptr;
0096 
0097     while (t) {
0098         if (dyn_cast<ImplicitCastExpr>(t) || dyn_cast<MaterializeTemporaryExpr>(t) || dyn_cast<CXXFunctionalCastExpr>(t)) {
0099             t = clazy::getFirstChild(t);
0100             continue;
0101         }
0102 
0103         if (!onlyTemporaries) {
0104             declRef = dyn_cast<DeclRefExpr>(t);
0105             if (declRef) {
0106                 break;
0107             }
0108         }
0109 
0110         temporaryExpr = dyn_cast<CXXBindTemporaryExpr>(t);
0111         if (temporaryExpr) {
0112             break;
0113         }
0114 
0115         break;
0116     }
0117 
0118     if (!temporaryExpr && !declRef) {
0119         return;
0120     }
0121 
0122     if (declRef) {
0123         auto *varDecl = dyn_cast<VarDecl>(declRef->getDecl());
0124         if (!varDecl || varDecl->isStaticLocal() || clazy::valueIsConst(varDecl->getType())) {
0125             return;
0126         }
0127 
0128         QualType qt = varDecl->getType();
0129         if (qt.isNull() || qt->isReferenceType()) {
0130             return;
0131         }
0132     } else if (temporaryExpr) {
0133         if (clazy::valueIsConst(temporaryExpr->getType())) {
0134             return;
0135         }
0136     }
0137 
0138     emitWarning(memberCall, "Returning data of temporary QByteArray");
0139 }