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(T, 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(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     LLVM_FALLTHROUGH;
124   case CommandLineIncludeScope:
125     EnteredCommandLineIncludeFiles++;
126     break;
127   case MainFileScope:
128     break;
129   }
130 
131   Scopes.push_back(Gen->getCGDebugInfo()->CreateTempMacroFile(getCurrentScope(),
132                                                               LineLoc, 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, const FileEntry *File,
171     StringRef SearchPath, StringRef RelativePath, const Module *Imported,
172     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(MacroNameTok.getLocation());
182   std::string NameBuffer, ValueBuffer;
183   llvm::raw_string_ostream Name(NameBuffer);
184   llvm::raw_string_ostream Value(ValueBuffer);
185   writeMacroDefinition(*Id, *MD->getMacroInfo(), PP, Name, Value);
186   Gen->getCGDebugInfo()->CreateMacro(getCurrentScope(),
187                                      llvm::dwarf::DW_MACINFO_define, location,
188                                      Name.str(), 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(MacroNameTok.getLocation());
196   Gen->getCGDebugInfo()->CreateMacro(getCurrentScope(),
197                                      llvm::dwarf::DW_MACINFO_undef, location,
198                                      Id->getName(), "");
199 }
200