File indexing completed on 2024-04-28 13:34:29
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 }