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

0001 /*
0002     SPDX-FileCopyrightText: 2018 Sergio Martins <smartins@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "empty-qstringliteral.h"
0008 #include "ClazyContext.h"
0009 #include "HierarchyUtils.h"
0010 #include "PreProcessorVisitor.h"
0011 #include "QtUtils.h"
0012 
0013 #include <clang/AST/Decl.h>
0014 #include <clang/AST/Expr.h>
0015 #include <clang/AST/Stmt.h>
0016 #include <clang/Basic/LLVM.h>
0017 #include <clang/Basic/SourceLocation.h>
0018 #include <llvm/ADT/StringRef.h>
0019 #include <llvm/Support/Casting.h>
0020 
0021 using namespace clang;
0022 
0023 EmptyQStringliteral::EmptyQStringliteral(const std::string &name, ClazyContext *context)
0024     : CheckBase(name, context)
0025 {
0026     context->enablePreprocessorVisitor();
0027 }
0028 
0029 void EmptyQStringliteral::VisitStmt(clang::Stmt *stmt)
0030 {
0031     if (!stmt->getBeginLoc().isMacroID()) {
0032         return;
0033     }
0034 
0035     const std::string filepath = static_cast<std::string>(sm().getFilename(sm().getExpansionLoc(stmt->getBeginLoc())));
0036     if (clazy::contains(filepath, ".rcc/qmlcache/")) {
0037         return; // This is an autogenerated file
0038     }
0039     if (auto splitted = clazy::splitString(filepath, '/'); !splitted.empty()) {
0040         if (std::string filename = splitted[splitted.size() - 1]; clazy::startsWith(filename, "ui_") && clazy::endsWith(filename, ".h")) {
0041             return; // Also ignore all ui files
0042         }
0043     }
0044 
0045     if (m_context->preprocessorVisitor && m_context->preprocessorVisitor->qtVersion() >= 60000) { // The AST looks very different in Qt6
0046         handleQt6StringLiteral(stmt);
0047     } else {
0048         handleQt5StringLiteral(stmt);
0049     }
0050 }
0051 
0052 void EmptyQStringliteral::handleQt6StringLiteral(clang::Stmt *stmt)
0053 {
0054     if (auto c = dyn_cast<CallExpr>(stmt)) {
0055         if (clazy::qualifiedMethodName(c->getDirectCallee()) == "QtPrivate::qMakeStringPrivate") {
0056             if (auto lt = clazy::getFirstChildOfType<StringLiteral>(c); lt && lt->getByteLength() == 0) {
0057                 emitWarning(stmt, "Use QLatin1String(\"\") or QString() instead of an empty QStringLiteral");
0058             }
0059         }
0060     }
0061 }
0062 
0063 void EmptyQStringliteral::handleQt5StringLiteral(clang::Stmt *stmt)
0064 {
0065     auto *declstm = dyn_cast<DeclStmt>(stmt);
0066     if (!declstm || !declstm->isSingleDecl()) {
0067         return;
0068     }
0069 
0070     auto *vd = dyn_cast<VarDecl>(declstm->getSingleDecl());
0071     if (!vd || clazy::name(vd) != "qstring_literal") {
0072         return;
0073     }
0074 
0075     Expr *expr = vd->getInit();
0076     auto *initListExpr = expr ? dyn_cast<InitListExpr>(expr) : nullptr;
0077     if (!initListExpr || initListExpr->getNumInits() != 2) {
0078         return;
0079     }
0080 
0081     Expr *init = initListExpr->getInit(1);
0082     auto *literal = init ? dyn_cast<StringLiteral>(init) : nullptr;
0083     if (!literal || literal->getByteLength() != 0) {
0084         return;
0085     }
0086 
0087     emitWarning(stmt, "Use QLatin1String(\"\") or QString() instead of an empty QStringLiteral");
0088 }