1 | //===--- MacroPPCallbacks.cpp ---------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file contains implementation for the macro preprocessors callbacks. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "MacroPPCallbacks.h" |
14 | #include "CGDebugInfo.h" |
15 | #include "clang/CodeGen/ModuleBuilder.h" |
16 | #include "clang/Lex/MacroInfo.h" |
17 | #include "clang/Lex/Preprocessor.h" |
18 | |
19 | using namespace clang; |
20 | |
21 | void MacroPPCallbacks::writeMacroDefinition(const IdentifierInfo &II, |
22 | const MacroInfo &MI, |
23 | Preprocessor &PP, raw_ostream &Name, |
24 | raw_ostream &Value) { |
25 | Name << II.getName(); |
26 | |
27 | if (MI.isFunctionLike()) { |
28 | Name << '('; |
29 | if (!MI.param_empty()) { |
30 | MacroInfo::param_iterator AI = MI.param_begin(), E = MI.param_end(); |
31 | for (; AI + 1 != E; ++AI) { |
32 | Name << (*AI)->getName(); |
33 | Name << ','; |
34 | } |
35 | |
36 | // Last argument. |
37 | if ((*AI)->getName() == "__VA_ARGS__" ) |
38 | Name << "..." ; |
39 | else |
40 | Name << (*AI)->getName(); |
41 | } |
42 | |
43 | if (MI.isGNUVarargs()) |
44 | // #define foo(x...) |
45 | Name << "..." ; |
46 | |
47 | Name << ')'; |
48 | } |
49 | |
50 | SmallString<128> SpellingBuffer; |
51 | bool First = true; |
52 | for (const auto &T : MI.tokens()) { |
53 | if (!First && T.hasLeadingSpace()) |
54 | Value << ' '; |
55 | |
56 | Value << PP.getSpelling(Tok: T, Buffer&: SpellingBuffer); |
57 | First = false; |
58 | } |
59 | } |
60 | |
61 | MacroPPCallbacks::MacroPPCallbacks(CodeGenerator *Gen, Preprocessor &PP) |
62 | : Gen(Gen), PP(PP), Status(NoScope) {} |
63 | |
64 | // This is the expected flow of enter/exit compiler and user files: |
65 | // - Main File Enter |
66 | // - <built-in> file enter |
67 | // {Compiler macro definitions} - (Line=0, no scope) |
68 | // - (Optional) <command line> file enter |
69 | // {Command line macro definitions} - (Line=0, no scope) |
70 | // - (Optional) <command line> file exit |
71 | // {Command line file includes} - (Line=0, Main file scope) |
72 | // {macro definitions and file includes} - (Line!=0, Parent scope) |
73 | // - <built-in> file exit |
74 | // {User code macro definitions and file includes} - (Line!=0, Parent scope) |
75 | |
76 | llvm::DIMacroFile *MacroPPCallbacks::getCurrentScope() { |
77 | if (Status == MainFileScope || Status == CommandLineIncludeScope) |
78 | return Scopes.back(); |
79 | return nullptr; |
80 | } |
81 | |
82 | SourceLocation MacroPPCallbacks::getCorrectLocation(SourceLocation Loc) { |
83 | if (Status == MainFileScope || EnteredCommandLineIncludeFiles) |
84 | return Loc; |
85 | |
86 | // While parsing skipped files, location of macros is invalid. |
87 | // Invalid location represents line zero. |
88 | return SourceLocation(); |
89 | } |
90 | |
91 | void MacroPPCallbacks::updateStatusToNextScope() { |
92 | switch (Status) { |
93 | case NoScope: |
94 | Status = InitializedScope; |
95 | break; |
96 | case InitializedScope: |
97 | Status = BuiltinScope; |
98 | break; |
99 | case BuiltinScope: |
100 | Status = CommandLineIncludeScope; |
101 | break; |
102 | case CommandLineIncludeScope: |
103 | Status = MainFileScope; |
104 | break; |
105 | case MainFileScope: |
106 | llvm_unreachable("There is no next scope, already in the final scope" ); |
107 | } |
108 | } |
109 | |
110 | void MacroPPCallbacks::FileEntered(SourceLocation Loc) { |
111 | SourceLocation LineLoc = getCorrectLocation(Loc: LastHashLoc); |
112 | switch (Status) { |
113 | case NoScope: |
114 | updateStatusToNextScope(); |
115 | break; |
116 | case InitializedScope: |
117 | updateStatusToNextScope(); |
118 | return; |
119 | case BuiltinScope: |
120 | if (PP.getSourceManager().isWrittenInCommandLineFile(Loc)) |
121 | return; |
122 | updateStatusToNextScope(); |
123 | [[fallthrough]]; |
124 | case CommandLineIncludeScope: |
125 | EnteredCommandLineIncludeFiles++; |
126 | break; |
127 | case MainFileScope: |
128 | break; |
129 | } |
130 | |
131 | Scopes.push_back(Elt: Gen->getCGDebugInfo()->CreateTempMacroFile(Parent: getCurrentScope(), |
132 | LineLoc, FileLoc: Loc)); |
133 | } |
134 | |
135 | void MacroPPCallbacks::FileExited(SourceLocation Loc) { |
136 | switch (Status) { |
137 | default: |
138 | llvm_unreachable("Do not expect to exit a file from current scope" ); |
139 | case BuiltinScope: |
140 | if (!PP.getSourceManager().isWrittenInBuiltinFile(Loc)) |
141 | // Skip next scope and change status to MainFileScope. |
142 | Status = MainFileScope; |
143 | return; |
144 | case CommandLineIncludeScope: |
145 | if (!EnteredCommandLineIncludeFiles) { |
146 | updateStatusToNextScope(); |
147 | return; |
148 | } |
149 | EnteredCommandLineIncludeFiles--; |
150 | break; |
151 | case MainFileScope: |
152 | break; |
153 | } |
154 | |
155 | Scopes.pop_back(); |
156 | } |
157 | |
158 | void MacroPPCallbacks::FileChanged(SourceLocation Loc, FileChangeReason Reason, |
159 | SrcMgr::CharacteristicKind FileType, |
160 | FileID PrevFID) { |
161 | // Only care about enter file or exit file changes. |
162 | if (Reason == EnterFile) |
163 | FileEntered(Loc); |
164 | else if (Reason == ExitFile) |
165 | FileExited(Loc); |
166 | } |
167 | |
168 | void MacroPPCallbacks::InclusionDirective( |
169 | SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName, |
170 | bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File, |
171 | StringRef SearchPath, StringRef RelativePath, const Module *SuggestedModule, |
172 | bool ModuleImported, SrcMgr::CharacteristicKind FileType) { |
173 | |
174 | // Record the line location of the current included file. |
175 | LastHashLoc = HashLoc; |
176 | } |
177 | |
178 | void MacroPPCallbacks::MacroDefined(const Token &MacroNameTok, |
179 | const MacroDirective *MD) { |
180 | IdentifierInfo *Id = MacroNameTok.getIdentifierInfo(); |
181 | SourceLocation location = getCorrectLocation(Loc: MacroNameTok.getLocation()); |
182 | std::string NameBuffer, ValueBuffer; |
183 | llvm::raw_string_ostream Name(NameBuffer); |
184 | llvm::raw_string_ostream Value(ValueBuffer); |
185 | writeMacroDefinition(II: *Id, MI: *MD->getMacroInfo(), PP, Name, Value); |
186 | Gen->getCGDebugInfo()->CreateMacro(Parent: getCurrentScope(), |
187 | MType: llvm::dwarf::DW_MACINFO_define, LineLoc: location, |
188 | Name: Name.str(), Value: Value.str()); |
189 | } |
190 | |
191 | void MacroPPCallbacks::MacroUndefined(const Token &MacroNameTok, |
192 | const MacroDefinition &MD, |
193 | const MacroDirective *Undef) { |
194 | IdentifierInfo *Id = MacroNameTok.getIdentifierInfo(); |
195 | SourceLocation location = getCorrectLocation(Loc: MacroNameTok.getLocation()); |
196 | Gen->getCGDebugInfo()->CreateMacro(Parent: getCurrentScope(), |
197 | MType: llvm::dwarf::DW_MACINFO_undef, LineLoc: location, |
198 | Name: Id->getName(), Value: "" ); |
199 | } |
200 | |