1/****************************************************************************
2 * Copyright (C) 2013-2016 Woboq GmbH
3 * Olivier Goffart <contact at woboq.com>
4 * https://woboq.com/
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <clang/Lex/Preprocessor.h>
21#include <clang/Basic/Version.h>
22#include <set>
23
24class MocPPCallbacks : public clang::PPCallbacks {
25 clang::Preprocessor &PP;
26
27 bool IncludeNotFoundSupressed = false;
28 bool ShouldWarnHeaderNotFound = false;
29 bool InQMOCRUN = false;
30 std::set<std::string> PossibleTags;
31 std::map<clang::SourceLocation, std::string> &Tags;
32
33public:
34
35 MocPPCallbacks(clang::Preprocessor &PP, std::map<clang::SourceLocation, std::string> &Tags) : PP(PP), Tags(Tags) {}
36
37 bool IsInMainFile = false;
38 void InjectQObjectDefs(clang::SourceLocation Loc);
39 void EnterMainFile(clang::StringRef Name);
40
41protected:
42#if CLANG_VERSION_MAJOR != 3 || CLANG_VERSION_MINOR >= 7
43 typedef const clang::MacroDefinition &MacroParam;
44 typedef const clang::MacroDirective *MacroParam2;
45#elif CLANG_VERSION_MAJOR != 3 || CLANG_VERSION_MINOR > 2
46 typedef const clang::MacroDirective *MacroParam;
47 typedef const clang::MacroDirective *MacroParam2;
48#else
49 typedef const clang::MacroInfo *MacroParam;
50 typedef MacroParam MacroParam2;
51#endif
52
53
54 void MacroUndefined(const clang::Token& MacroNameTok, MacroParam
55#if CLANG_VERSION_MAJOR >= 5
56 , const clang::MacroDirective *
57#endif
58 ) override {
59 //Workaround to get moc's test to compile
60 if (MacroNameTok.getIdentifierInfo()->getName() == "QT_NO_KEYWORDS") {
61 //re-inject qobjectdefs
62 InjectQObjectDefs(MacroNameTok.getLocation());
63 }
64 }
65
66 void FileChanged(clang::SourceLocation Loc, FileChangeReason Reason, clang::SrcMgr::CharacteristicKind FileType,
67 clang::FileID PrevFID) override;
68 bool FileNotFound(llvm::StringRef FileName, llvm::SmallVectorImpl< char >& RecoveryPath) override;
69 void InclusionDirective(clang::SourceLocation HashLoc, const clang::Token& IncludeTok,
70 llvm::StringRef FileName, bool IsAngled, clang::CharSourceRange FilenameRange,
71 const clang::FileEntry* File, llvm::StringRef SearchPath, llvm::StringRef RelativePath,
72 const clang::Module* Imported) override;
73
74 void Defined(const clang::Token& MacroNameTok
75#if CLANG_VERSION_MAJOR != 3 || CLANG_VERSION_MINOR > 2
76 ,MacroParam = {}
77#endif
78#if CLANG_VERSION_MAJOR != 3 || CLANG_VERSION_MINOR > 3
79 , clang::SourceRange = {}
80#endif
81 ) override {
82 if (MacroNameTok.getIdentifierInfo()->getName() != "Q_MOC_RUN")
83 return;
84 auto F = PP.getSourceManager().getFileEntryForID(PP.getSourceManager().getFileID(MacroNameTok.getLocation()));
85 if (!F) return;
86 llvm::StringRef name = F->getName();
87 if (name.endswith("qobjectdefs.h") || name.endswith("qglobal.h"))
88 return;
89 InQMOCRUN = true;
90 }
91 void Ifdef(clang::SourceLocation Loc, const clang::Token& MacroNameTok
92#if CLANG_VERSION_MAJOR != 3 || CLANG_VERSION_MINOR > 2
93 ,MacroParam
94#endif
95 ) override { Defined(MacroNameTok); }
96 void Ifndef(clang::SourceLocation Loc, const clang::Token& MacroNameTok
97#if CLANG_VERSION_MAJOR != 3 || CLANG_VERSION_MINOR > 2
98 ,MacroParam
99#endif
100 ) override { Defined(MacroNameTok); }
101
102 void Endif(clang::SourceLocation Loc, clang::SourceLocation IfLoc) override {
103 InQMOCRUN = false;
104 //TODO: handle embedded Ifs
105 }
106
107 void MacroDefined(const clang::Token& MacroNameTok, MacroParam2) override {
108 if (!InQMOCRUN)
109 return;
110 PossibleTags.insert(MacroNameTok.getIdentifierInfo()->getName());
111 }
112
113
114 void MacroExpands(const clang::Token& MacroNameTok, MacroParam, clang::SourceRange Range
115#if CLANG_VERSION_MAJOR != 3 || CLANG_VERSION_MINOR > 2
116 , const clang::MacroArgs *
117#endif
118 ) override {
119 if (InQMOCRUN) return;
120 if (PossibleTags.count(MacroNameTok.getIdentifierInfo()->getName())) {
121 clang::SourceLocation FileLoc = PP.getSourceManager().getFileLoc(MacroNameTok.getLocation());
122 Tags.insert({FileLoc, MacroNameTok.getIdentifierInfo()->getName()});
123 }
124 }
125
126};
127