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

0001 /*
0002     SPDX-FileCopyrightText: 2017 Klarälvdalens Datakonsult AB a KDAB Group company info@kdab.com
0003     SPDX-FileContributor: SĂ©rgio Martins <sergio.martins@kdab.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "connect-not-normalized.h"
0009 #include "ClazyContext.h"
0010 #include "HierarchyUtils.h"
0011 #include "NormalizedSignatureUtils.h"
0012 #include "StringUtils.h"
0013 
0014 #include <clang/AST/Decl.h>
0015 #include <clang/AST/DeclCXX.h>
0016 #include <clang/AST/Expr.h>
0017 #include <clang/AST/ExprCXX.h>
0018 #include <clang/AST/ParentMap.h>
0019 #include <clang/AST/Stmt.h>
0020 #include <clang/Basic/LLVM.h>
0021 #include <llvm/ADT/StringRef.h>
0022 #include <llvm/Support/Casting.h>
0023 
0024 using namespace clang;
0025 
0026 ConnectNotNormalized::ConnectNotNormalized(const std::string &name, ClazyContext *context)
0027     : CheckBase(name, context, Option_CanIgnoreIncludes)
0028 {
0029 }
0030 
0031 void ConnectNotNormalized::VisitStmt(clang::Stmt *stmt)
0032 {
0033     handleQ_ARG(stmt) || handleConnect(dyn_cast<CallExpr>(stmt));
0034 }
0035 
0036 bool ConnectNotNormalized::handleQ_ARG(clang::Stmt *stmt)
0037 {
0038     if (auto *casted = dyn_cast<CallExpr>(stmt); casted && casted->getNumArgs() == 2) {
0039         if (auto *func = casted->getDirectCallee()) {
0040             const std::string retTypeName = func->getReturnType().getAsString(lo());
0041             if (retTypeName == "QMetaMethodArgument" || retTypeName == "QMetaMethodReturnArgument") {
0042                 auto *literal = clazy::getFirstChildOfType2<StringLiteral>(casted->getArg(0));
0043                 return literal ? checkNormalizedLiteral(literal, casted) : false;
0044             }
0045         }
0046     }
0047     auto *expr = dyn_cast<CXXConstructExpr>(stmt);
0048     if (!expr || expr->getNumArgs() != 2) {
0049         return false;
0050     }
0051 
0052     CXXConstructorDecl *ctor = expr->getConstructor();
0053     if (!ctor) {
0054         return false;
0055     }
0056 
0057     auto name = ctor->getNameAsString();
0058     if (name != "QArgument" && name != "QReturnArgument") {
0059         return false;
0060     }
0061 
0062     auto *sl = clazy::getFirstChildOfType2<clang::StringLiteral>(expr->getArg(0));
0063     return sl && checkNormalizedLiteral(sl, expr);
0064 }
0065 bool ConnectNotNormalized::checkNormalizedLiteral(clang::StringLiteral *sl, clang::Expr *expr)
0066 {
0067     const std::string original = sl->getString().str();
0068     const std::string normalized = clazy::normalizedType(original.c_str());
0069 
0070     if (original == normalized) {
0071         return false;
0072     }
0073 
0074     emitWarning(expr, "Signature is not normalized. Use " + normalized + " instead of " + original);
0075     return true;
0076 }
0077 
0078 bool ConnectNotNormalized::handleConnect(CallExpr *callExpr)
0079 {
0080     if (!callExpr) {
0081         return false;
0082     }
0083 
0084     FunctionDecl *func = callExpr->getDirectCallee();
0085     if (!func || func->getNumParams() != 1 || clazy::name(func) != "qFlagLocation") {
0086         return false;
0087     }
0088 
0089     {
0090         // Only warn in connect statements, not disconnect, since there there's no optimization in Qt's side
0091         auto *parentCallExpr = clazy::getFirstParentOfType<CallExpr>(m_context->parentMap, m_context->parentMap->getParent(callExpr), -1);
0092         if (!parentCallExpr) {
0093             return false;
0094         }
0095 
0096         FunctionDecl *parentFunc = parentCallExpr->getDirectCallee();
0097         if (!parentFunc || clazy::name(parentFunc) != "connect") {
0098             return false;
0099         }
0100     }
0101 
0102     Expr *arg1 = callExpr->getArg(0);
0103     auto *sl = clazy::getFirstChildOfType2<clang::StringLiteral>(arg1);
0104     if (!sl) {
0105         return false;
0106     }
0107     std::string original = sl->getString().str();
0108     std::string normalized = clazy::normalizedSignature(original.c_str());
0109 
0110     // discard the junk after '\0'
0111     normalized = std::string(normalized.c_str());
0112     original = std::string(original.c_str());
0113 
0114     if (original == normalized) {
0115         return false;
0116     }
0117 
0118     // Remove first digit
0119     normalized.erase(0, 1);
0120     original.erase(0, 1);
0121 
0122     emitWarning(callExpr->getBeginLoc(), "Signature is not normalized. Use " + normalized + " instead of " + original);
0123     return true;
0124 }