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 }