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
19using namespace clang;
20
21void 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
61MacroPPCallbacks::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
76llvm::DIMacroFile *MacroPPCallbacks::getCurrentScope() {
77  if (Status == MainFileScope || Status == CommandLineIncludeScope)
78    return Scopes.back();
79  return nullptr;
80}
81
82SourceLocation 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
91void 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
110void 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    [[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
135void 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
158void 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
168void MacroPPCallbacks::InclusionDirective(
169    SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
170    bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef 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
178void 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
191void 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