File indexing completed on 2024-04-28 16:57:51
0001 /* 0002 This file is part of the clazy static checker. 0003 0004 Copyright (C) 2017 Sergio Martins <smartins@kde.org> 0005 0006 This library is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU Library General Public 0008 License as published by the Free Software Foundation; either 0009 version 2 of the License, or (at your option) any later version. 0010 0011 This library is distributed in the hope that it will be useful, 0012 but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0014 Library General Public License for more details. 0015 0016 You should have received a copy of the GNU Library General Public License 0017 along with this library; see the file COPYING.LIB. If not, write to 0018 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0019 Boston, MA 02110-1301, USA. 0020 */ 0021 0022 #include "PreProcessorVisitor.h" 0023 #include "MacroUtils.h" 0024 0025 #include <clang/Frontend/CompilerInstance.h> 0026 #include <clang/Lex/Preprocessor.h> 0027 #include <clang/Lex/MacroInfo.h> 0028 #include <clang/Basic/IdentifierTable.h> 0029 #include <clang/Basic/SourceLocation.h> 0030 #include <clang/Basic/SourceManager.h> 0031 #include <clang/Lex/PPCallbacks.h> 0032 #include <clang/Lex/Token.h> 0033 #include <llvm/ADT/ArrayRef.h> 0034 0035 #include <stdlib.h> 0036 #include <memory> 0037 0038 using namespace clang; 0039 using namespace std; 0040 0041 PreProcessorVisitor::PreProcessorVisitor(const clang::CompilerInstance &ci) 0042 : clang::PPCallbacks() 0043 , m_ci(ci) 0044 , m_sm(ci.getSourceManager()) 0045 { 0046 Preprocessor &pi = m_ci.getPreprocessor(); 0047 pi.addPPCallbacks(std::unique_ptr<PPCallbacks>(this)); 0048 0049 // This catches -DQT_NO_KEYWORDS passed to compiler. In MacroExpands() we catch when defined via in code 0050 m_isQtNoKeywords = clazy::isPredefined(ci.getPreprocessorOpts(), "QT_NO_KEYWORDS"); 0051 } 0052 0053 bool PreProcessorVisitor::isBetweenQtNamespaceMacros(SourceLocation loc) 0054 { 0055 if (loc.isInvalid()) 0056 return false; 0057 0058 if (loc.isMacroID()) 0059 loc = m_sm.getExpansionLoc(loc); 0060 0061 uint fileId = m_sm.getFileID(loc).getHashValue(); 0062 0063 vector<SourceRange> &pairs = m_q_namespace_macro_locations[fileId]; 0064 for (SourceRange &pair : pairs) { 0065 if (pair.getBegin().isInvalid() || pair.getEnd().isInvalid()) { 0066 //llvm::errs() << "PreProcessorVisitor::isBetweenQtNamespaceMacros Found invalid location\n"; 0067 continue; // shouldn't happen 0068 } 0069 0070 if (m_sm.isBeforeInSLocAddrSpace(pair.getBegin(), loc) && 0071 m_sm.isBeforeInSLocAddrSpace(loc, pair.getEnd())) 0072 return true; 0073 } 0074 0075 return false; 0076 } 0077 0078 bool PreProcessorVisitor::hasInclude(const std::string &fileName, bool IsAngled) const 0079 { 0080 auto it = std::find_if(m_includeInfo.cbegin(), m_includeInfo.cend(), [&] (const IncludeInfo& info) { 0081 return info.fileName == fileName && info.IsAngled == IsAngled; 0082 }); 0083 return (it != m_includeInfo.cend()); 0084 } 0085 0086 SourceLocation PreProcessorVisitor::endOfIncludeSection() const 0087 { 0088 if (m_includeInfo.empty()) 0089 return {}; 0090 return m_includeInfo.back().filenameRange.getEnd(); 0091 } 0092 0093 std::string PreProcessorVisitor::getTokenSpelling(const MacroDefinition &def) const 0094 { 0095 if (!def) 0096 return {}; 0097 0098 MacroInfo *info = def.getMacroInfo(); 0099 if (!info) 0100 return {}; 0101 0102 const Preprocessor &pp = m_ci.getPreprocessor(); 0103 string result; 0104 for (const auto &tok : info->tokens()) 0105 result += pp.getSpelling(tok); 0106 0107 return result; 0108 } 0109 0110 void PreProcessorVisitor::updateQtVersion() 0111 { 0112 if (m_qtMajorVersion == -1 || m_qtPatchVersion == -1 || m_qtMinorVersion == -1) { 0113 m_qtVersion = -1; 0114 } else { 0115 m_qtVersion = m_qtPatchVersion + m_qtMinorVersion * 100 + m_qtMajorVersion * 10000; 0116 } 0117 } 0118 0119 void PreProcessorVisitor::handleQtNamespaceMacro(SourceLocation loc, StringRef name) 0120 { 0121 const bool isBegin = name == "QT_BEGIN_NAMESPACE"; 0122 uint fileId = m_sm.getFileID(loc).getHashValue(); 0123 vector<SourceRange> &pairs = m_q_namespace_macro_locations[fileId]; 0124 0125 if (isBegin) { 0126 pairs.push_back(SourceRange(loc, {})); 0127 } else { 0128 if (pairs.empty()) { 0129 // llvm::errs() << "FOO Received end!!"; 0130 } else { 0131 SourceRange &range = pairs[pairs.size() - 1]; 0132 if (range.getBegin().isInvalid()) { 0133 // llvm::errs() << "FOO Error received end before a begin\n"; 0134 } else { 0135 range.setEnd(loc); 0136 } 0137 } 0138 } 0139 } 0140 0141 static int stringToNumber(const string &str) 0142 { 0143 if (str.empty()) 0144 return -1; 0145 0146 return atoi(str.c_str()); 0147 } 0148 0149 void PreProcessorVisitor::MacroExpands(const Token &MacroNameTok, const MacroDefinition &def, 0150 SourceRange range, const MacroArgs *) 0151 { 0152 IdentifierInfo *ii = MacroNameTok.getIdentifierInfo(); 0153 if (!ii) 0154 return; 0155 0156 if (ii->getName() == "QT_BEGIN_NAMESPACE" || ii->getName() == "QT_END_NAMESPACE") { 0157 handleQtNamespaceMacro(range.getBegin(), ii->getName()); 0158 return; 0159 } 0160 0161 if (!m_isQtNoKeywords && ii->getName() == "QT_NO_KEYWORDS") { 0162 m_isQtNoKeywords = true; 0163 return; 0164 } 0165 0166 if (m_qtVersion != -1) 0167 return; 0168 0169 auto name = ii->getName(); 0170 if (name == "QT_VERSION_MAJOR") { 0171 m_qtMajorVersion = stringToNumber(getTokenSpelling(def)); 0172 updateQtVersion(); 0173 } 0174 0175 if (name == "QT_VERSION_MINOR") { 0176 m_qtMinorVersion = stringToNumber(getTokenSpelling(def)); 0177 updateQtVersion(); 0178 } 0179 0180 if (name == "QT_VERSION_PATCH") { 0181 m_qtPatchVersion = stringToNumber(getTokenSpelling(def)); 0182 updateQtVersion(); 0183 } 0184 } 0185 0186 void PreProcessorVisitor::InclusionDirective (clang::SourceLocation, const clang::Token &, 0187 clang::StringRef FileName, bool IsAngled, clang::CharSourceRange FilenameRange, 0188 const clang::FileEntry *, clang::StringRef, clang::StringRef, 0189 const clang::Module *, clang::SrcMgr::CharacteristicKind) 0190 { 0191 if (m_ci.getPreprocessor().isInPrimaryFile() && !clazy::endsWith(FileName.str(), ".moc")) { 0192 m_includeInfo.push_back(IncludeInfo{FileName, IsAngled, FilenameRange}); 0193 } 0194 }