/* This file is part of the clazy static checker. Copyright (C) 2017 Sergio Martins This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "PreProcessorVisitor.h" #include "MacroUtils.h" #include #include #include using namespace clang; using namespace std; PreProcessorVisitor::PreProcessorVisitor(const clang::CompilerInstance &ci) : clang::PPCallbacks() , m_ci(ci) , m_sm(ci.getSourceManager()) { Preprocessor &pi = m_ci.getPreprocessor(); pi.addPPCallbacks(std::unique_ptr(this)); // This catches -DQT_NO_KEYWORDS passed to compiler. In MacroExpands() we catch when defined via in code m_isQtNoKeywords = clazy::isPredefined(ci.getPreprocessorOpts(), "QT_NO_KEYWORDS"); } bool PreProcessorVisitor::isBetweenQtNamespaceMacros(SourceLocation loc) { if (loc.isInvalid()) return false; if (loc.isMacroID()) loc = m_sm.getExpansionLoc(loc); uint fileId = m_sm.getFileID(loc).getHashValue(); vector &pairs = m_q_namespace_macro_locations[fileId]; for (SourceRange &pair : pairs) { if (pair.getBegin().isInvalid() || pair.getEnd().isInvalid()) { //llvm::errs() << "PreProcessorVisitor::isBetweenQtNamespaceMacros Found invalid location\n"; continue; // shouldn't happen } if (m_sm.isBeforeInSLocAddrSpace(pair.getBegin(), loc) && m_sm.isBeforeInSLocAddrSpace(loc, pair.getEnd())) return true; } return false; } std::string PreProcessorVisitor::getTokenSpelling(const MacroDefinition &def) const { if (!def) return {}; MacroInfo *info = def.getMacroInfo(); if (!info) return {}; const Preprocessor &pp = m_ci.getPreprocessor(); string result; for (const auto &tok : info->tokens()) result += pp.getSpelling(tok); return result; } void PreProcessorVisitor::updateQtVersion() { if (m_qtMajorVersion == -1 || m_qtPatchVersion == -1 || m_qtMinorVersion == -1) { m_qtVersion = -1; } else { m_qtVersion = m_qtPatchVersion + m_qtMinorVersion * 100 + m_qtMajorVersion * 10000; } } void PreProcessorVisitor::handleQtNamespaceMacro(SourceLocation loc, StringRef name) { const bool isBegin = name == "QT_BEGIN_NAMESPACE"; uint fileId = m_sm.getFileID(loc).getHashValue(); vector &pairs = m_q_namespace_macro_locations[fileId]; if (isBegin) { pairs.push_back(SourceRange(loc, {})); } else { if (pairs.empty()) { // llvm::errs() << "FOO Received end!!"; } else { SourceRange &range = pairs[pairs.size() - 1]; if (range.getBegin().isInvalid()) { // llvm::errs() << "FOO Error received end before a begin\n"; } else { range.setEnd(loc); } } } } static int stringToNumber(const string &str) { if (str.empty()) return -1; return atoi(str.c_str()); } void PreProcessorVisitor::MacroExpands(const Token &MacroNameTok, const MacroDefinition &def, SourceRange range, const MacroArgs *) { IdentifierInfo *ii = MacroNameTok.getIdentifierInfo(); if (!ii) return; if (ii->getName() == "QT_BEGIN_NAMESPACE" || ii->getName() == "QT_END_NAMESPACE") { handleQtNamespaceMacro(range.getBegin(), ii->getName()); return; } if (!m_isQtNoKeywords && ii->getName() == "QT_NO_KEYWORDS") { m_isQtNoKeywords = true; return; } if (m_qtVersion != -1) return; auto name = ii->getName(); if (name == "QT_VERSION_MAJOR") { m_qtMajorVersion = stringToNumber(getTokenSpelling(def)); updateQtVersion(); } if (name == "QT_VERSION_MINOR") { m_qtMinorVersion = stringToNumber(getTokenSpelling(def)); updateQtVersion(); } if (name == "QT_VERSION_PATCH") { m_qtPatchVersion = stringToNumber(getTokenSpelling(def)); updateQtVersion(); } }