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
73#if CLANG_VERSION_MAJOR >= 7
74 , clang::SrcMgr::CharacteristicKind
75#endif
76 ) override;
77
78 void Defined(const clang::Token& MacroNameTok
79#if CLANG_VERSION_MAJOR != 3 || CLANG_VERSION_MINOR > 2
80 ,MacroParam = {}
81#endif
82#if CLANG_VERSION_MAJOR != 3 || CLANG_VERSION_MINOR > 3
83 , clang::SourceRange = {}
84#endif
85 ) override {
86 if (MacroNameTok.getIdentifierInfo()->getName() != "Q_MOC_RUN")
87 return;
88 auto F = PP.getSourceManager().getFileEntryForID(PP.getSourceManager().getFileID(MacroNameTok.getLocation()));
89 if (!F) return;
90 llvm::StringRef name = F->getName();
91 if (name.endswith("qobjectdefs.h") || name.endswith("qglobal.h"))
92 return;
93 InQMOCRUN = true;
94 }
95 void Ifdef(clang::SourceLocation Loc, const clang::Token& MacroNameTok
96#if CLANG_VERSION_MAJOR != 3 || CLANG_VERSION_MINOR > 2
97 ,MacroParam
98#endif
99 ) override { Defined(MacroNameTok); }
100 void Ifndef(clang::SourceLocation Loc, const clang::Token& MacroNameTok
101#if CLANG_VERSION_MAJOR != 3 || CLANG_VERSION_MINOR > 2
102 ,MacroParam
103#endif
104 ) override { Defined(MacroNameTok); }
105
106 void Endif(clang::SourceLocation Loc, clang::SourceLocation IfLoc) override {
107 InQMOCRUN = false;
108 //TODO: handle embedded Ifs
109 }
110
111 void MacroDefined(const clang::Token& MacroNameTok, MacroParam2) override {
112 if (!InQMOCRUN)
113 return;
114 PossibleTags.insert(MacroNameTok.getIdentifierInfo()->getName());
115 }
116
117
118 void MacroExpands(const clang::Token& MacroNameTok, MacroParam, clang::SourceRange Range
119#if CLANG_VERSION_MAJOR != 3 || CLANG_VERSION_MINOR > 2
120 , const clang::MacroArgs *
121#endif
122 ) override {
123 if (InQMOCRUN) return;
124 if (PossibleTags.count(MacroNameTok.getIdentifierInfo()->getName())) {
125 clang::SourceLocation FileLoc = PP.getSourceManager().getFileLoc(MacroNameTok.getLocation());
126 Tags.insert({FileLoc, MacroNameTok.getIdentifierInfo()->getName()});
127 }
128 }
129
130};
131