File indexing completed on 2024-04-14 05:32:07

0001 /*
0002     SPDX-FileCopyrightText: 2017 Sergio Martins <smartins@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "PreProcessorVisitor.h"
0008 #include "MacroUtils.h"
0009 
0010 #include <clang/Basic/IdentifierTable.h>
0011 #include <clang/Basic/SourceLocation.h>
0012 #include <clang/Basic/SourceManager.h>
0013 #include <clang/Frontend/CompilerInstance.h>
0014 #include <clang/Lex/MacroInfo.h>
0015 #include <clang/Lex/PPCallbacks.h>
0016 #include <clang/Lex/Preprocessor.h>
0017 #include <clang/Lex/Token.h>
0018 #include <llvm/ADT/ArrayRef.h>
0019 
0020 #include <memory>
0021 #include <stdlib.h>
0022 
0023 using namespace clang;
0024 
0025 PreProcessorVisitor::PreProcessorVisitor(const clang::CompilerInstance &ci)
0026     : clang::PPCallbacks()
0027     , m_ci(ci)
0028     , m_sm(ci.getSourceManager())
0029 {
0030     Preprocessor &pi = m_ci.getPreprocessor();
0031     pi.addPPCallbacks(std::unique_ptr<PPCallbacks>(this));
0032 
0033     // This catches -DQT_NO_KEYWORDS passed to compiler. In MacroExpands() we catch when defined via in code
0034     m_isQtNoKeywords = clazy::isPredefined(ci.getPreprocessorOpts(), "QT_NO_KEYWORDS");
0035 }
0036 
0037 bool PreProcessorVisitor::isBetweenQtNamespaceMacros(SourceLocation loc)
0038 {
0039     if (loc.isInvalid()) {
0040         return false;
0041     }
0042 
0043     if (loc.isMacroID()) {
0044         loc = m_sm.getExpansionLoc(loc);
0045     }
0046 
0047     uint fileId = m_sm.getFileID(loc).getHashValue();
0048 
0049     std::vector<SourceRange> &pairs = m_q_namespace_macro_locations[fileId];
0050     for (SourceRange &pair : pairs) {
0051         if (pair.getBegin().isInvalid() || pair.getEnd().isInvalid()) {
0052             // llvm::errs() << "PreProcessorVisitor::isBetweenQtNamespaceMacros Found invalid location\n";
0053             continue; // shouldn't happen
0054         }
0055 
0056         if (m_sm.isBeforeInSLocAddrSpace(pair.getBegin(), loc) && m_sm.isBeforeInSLocAddrSpace(loc, pair.getEnd())) {
0057             return true;
0058         }
0059     }
0060 
0061     return false;
0062 }
0063 
0064 bool PreProcessorVisitor::hasInclude(const std::string &fileName, bool IsAngled) const
0065 {
0066     auto it = std::find_if(m_includeInfo.cbegin(), m_includeInfo.cend(), [&](const IncludeInfo &info) {
0067         return info.fileName == fileName && info.IsAngled == IsAngled;
0068     });
0069     return (it != m_includeInfo.cend());
0070 }
0071 
0072 SourceLocation PreProcessorVisitor::endOfIncludeSection() const
0073 {
0074     if (m_includeInfo.empty()) {
0075         return {};
0076     }
0077     return m_includeInfo.back().filenameRange.getEnd();
0078 }
0079 
0080 std::string PreProcessorVisitor::getTokenSpelling(const MacroDefinition &def) const
0081 {
0082     if (!def) {
0083         return {};
0084     }
0085 
0086     MacroInfo *info = def.getMacroInfo();
0087     if (!info) {
0088         return {};
0089     }
0090 
0091     const Preprocessor &pp = m_ci.getPreprocessor();
0092     std::string result;
0093     for (const auto &tok : info->tokens()) {
0094         result += pp.getSpelling(tok);
0095     }
0096 
0097     return result;
0098 }
0099 
0100 void PreProcessorVisitor::updateQtVersion()
0101 {
0102     if (m_qtMajorVersion == -1 || m_qtPatchVersion == -1 || m_qtMinorVersion == -1) {
0103         m_qtVersion = -1;
0104     } else {
0105         m_qtVersion = m_qtPatchVersion + m_qtMinorVersion * 100 + m_qtMajorVersion * 10000;
0106     }
0107 }
0108 
0109 void PreProcessorVisitor::handleQtNamespaceMacro(SourceLocation loc, StringRef name)
0110 {
0111     const bool isBegin = name == "QT_BEGIN_NAMESPACE";
0112     uint fileId = m_sm.getFileID(loc).getHashValue();
0113     std::vector<SourceRange> &pairs = m_q_namespace_macro_locations[fileId];
0114 
0115     if (isBegin) {
0116         pairs.push_back(SourceRange(loc, {}));
0117     } else {
0118         if (pairs.empty()) {
0119             // llvm::errs() << "FOO Received end!!";
0120         } else {
0121             SourceRange &range = pairs[pairs.size() - 1];
0122             if (range.getBegin().isInvalid()) {
0123                 // llvm::errs() << "FOO Error received end before a begin\n";
0124             } else {
0125                 range.setEnd(loc);
0126             }
0127         }
0128     }
0129 }
0130 
0131 static int stringToNumber(const std::string &str)
0132 {
0133     if (str.empty()) {
0134         return -1;
0135     }
0136 
0137     return atoi(str.c_str());
0138 }
0139 
0140 void PreProcessorVisitor::MacroExpands(const Token &MacroNameTok, const MacroDefinition &def, SourceRange range, const MacroArgs *)
0141 {
0142     IdentifierInfo *ii = MacroNameTok.getIdentifierInfo();
0143     if (!ii) {
0144         return;
0145     }
0146 
0147     if (ii->getName() == "QT_BEGIN_NAMESPACE" || ii->getName() == "QT_END_NAMESPACE") {
0148         handleQtNamespaceMacro(range.getBegin(), ii->getName());
0149         return;
0150     }
0151 
0152     if (!m_isQtNoKeywords && ii->getName() == "QT_NO_KEYWORDS") {
0153         m_isQtNoKeywords = true;
0154         return;
0155     }
0156 
0157     if (m_qtVersion != -1) {
0158         return;
0159     }
0160 
0161     auto name = ii->getName();
0162     if (name == "QT_VERSION_MAJOR") {
0163         m_qtMajorVersion = stringToNumber(getTokenSpelling(def));
0164         updateQtVersion();
0165     }
0166 
0167     if (name == "QT_VERSION_MINOR") {
0168         m_qtMinorVersion = stringToNumber(getTokenSpelling(def));
0169         updateQtVersion();
0170     }
0171 
0172     if (name == "QT_VERSION_PATCH") {
0173         m_qtPatchVersion = stringToNumber(getTokenSpelling(def));
0174         updateQtVersion();
0175     }
0176 }
0177 
0178 void PreProcessorVisitor::InclusionDirective(clang::SourceLocation,
0179                                              const clang::Token &,
0180                                              clang::StringRef FileName,
0181                                              bool IsAngled,
0182                                              clang::CharSourceRange FilenameRange,
0183                                              clazy::OptionalFileEntryRef,
0184                                              clang::StringRef,
0185                                              clang::StringRef,
0186                                              const clang::Module *,
0187                                              clang::SrcMgr::CharacteristicKind)
0188 {
0189     if (m_ci.getPreprocessor().isInPrimaryFile() && !clazy::endsWith(FileName.str(), ".moc")) {
0190         m_includeInfo.push_back(IncludeInfo{FileName, IsAngled, FilenameRange});
0191     }
0192 }