File indexing completed on 2024-05-12 05:41:00

0001 /*
0002     SPDX-FileCopyrightText: 2015 Sergio Martins <smartins@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "missing-qobject-macro.h"
0008 #include "ClazyContext.h"
0009 #include "FixItUtils.h"
0010 #include "PreProcessorVisitor.h"
0011 #include "QtUtils.h"
0012 
0013 #include <clang/AST/DeclBase.h>
0014 #include <clang/AST/DeclCXX.h>
0015 #include <clang/Basic/IdentifierTable.h>
0016 #include <clang/Basic/LLVM.h>
0017 #include <clang/Basic/SourceManager.h>
0018 #include <clang/Lex/Token.h>
0019 #include <filesystem>
0020 #include <llvm/ADT/StringRef.h>
0021 #include <llvm/Support/Casting.h>
0022 
0023 namespace clang
0024 {
0025 class MacroInfo;
0026 } // namespace clang
0027 
0028 using namespace clang;
0029 
0030 MissingQObjectMacro::MissingQObjectMacro(const std::string &name, ClazyContext *context)
0031     : CheckBase(name, context)
0032 {
0033     enablePreProcessorCallbacks();
0034     context->enablePreprocessorVisitor();
0035 }
0036 
0037 void MissingQObjectMacro::VisitMacroExpands(const clang::Token &MacroNameTok, const clang::SourceRange &range, const MacroInfo *)
0038 {
0039     IdentifierInfo *ii = MacroNameTok.getIdentifierInfo();
0040     if (ii && ii->getName() == "Q_OBJECT") {
0041         registerQ_OBJECT(range.getBegin());
0042     }
0043 }
0044 
0045 void MissingQObjectMacro::VisitDecl(clang::Decl *decl)
0046 {
0047     auto *record = dyn_cast<CXXRecordDecl>(decl);
0048     if (!record || !record->hasDefinition() || record->getDefinition() != record || !clazy::isQObject(record)) {
0049         return;
0050     }
0051 
0052     if (record->getDescribedClassTemplate() != nullptr) { // moc doesn't accept Q_OBJECT in templates
0053         return;
0054     }
0055 
0056     if (m_context->usingPreCompiledHeaders()) {
0057         return;
0058     }
0059 
0060     const SourceLocation startLoc = decl->getBeginLoc();
0061 
0062     for (const SourceLocation &loc : m_qobjectMacroLocations) {
0063         if (sm().getFileID(loc) != sm().getFileID(startLoc)) {
0064             continue; // Different file
0065         }
0066 
0067         if (sm().isBeforeInSLocAddrSpace(startLoc, loc) && sm().isBeforeInSLocAddrSpace(loc, decl->getEndLoc())) {
0068             return; // We found a Q_OBJECT after start and before end, it's ours.
0069         }
0070     }
0071 
0072     std::vector<FixItHint> fixits;
0073     const SourceLocation pos = record->getBraceRange().getBegin().getLocWithOffset(1);
0074     fixits.push_back(clazy::createInsertion(pos, "\n\tQ_OBJECT"));
0075 
0076     const std::string fileName = static_cast<std::string>(sm().getFilename(startLoc));
0077     if (clazy::endsWith(fileName, ".cpp")) {
0078         const std::string basename = std::filesystem::path(fileName).stem().string();
0079 
0080         if (!m_hasAddedMocFile && !m_context->preprocessorVisitor->hasInclude(basename + ".moc", false)) {
0081             const SourceLocation pos = sm().getLocForEndOfFile(sm().getFileID(startLoc));
0082             fixits.push_back(clazy::createInsertion(pos, "\n#include \"" + basename + ".moc\"\n"));
0083             m_hasAddedMocFile = true;
0084         }
0085     }
0086 
0087     emitWarning(startLoc, record->getQualifiedNameAsString() + " is missing a Q_OBJECT macro", fixits);
0088 }
0089 
0090 void MissingQObjectMacro::registerQ_OBJECT(SourceLocation loc)
0091 {
0092     m_qobjectMacroLocations.push_back(loc);
0093 }