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 }