1 //===--- MacroExpander.cpp - Format C++ code --------------------*- C++ -*-===//
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 /// \file
10 /// This file contains the implementation of MacroExpander, which handles macro
11 /// configuration and expansion while formatting.
12 ///
13 //===----------------------------------------------------------------------===//
14
15 #include "Macros.h"
16
17 #include "Encoding.h"
18 #include "FormatToken.h"
19 #include "FormatTokenLexer.h"
20 #include "clang/Basic/TokenKinds.h"
21 #include "clang/Format/Format.h"
22 #include "clang/Lex/HeaderSearch.h"
23 #include "clang/Lex/HeaderSearchOptions.h"
24 #include "clang/Lex/Lexer.h"
25 #include "clang/Lex/ModuleLoader.h"
26 #include "clang/Lex/Preprocessor.h"
27 #include "clang/Lex/PreprocessorOptions.h"
28 #include "llvm/ADT/StringSet.h"
29 #include "llvm/Support/ErrorHandling.h"
30
31 namespace clang {
32 namespace format {
33
34 struct MacroExpander::Definition {
35 StringRef Name;
36 SmallVector<FormatToken *, 8> Params;
37 SmallVector<FormatToken *, 8> Body;
38
39 // Map from each argument's name to its position in the argument list.
40 // With "M(x, y) x + y":
41 // x -> 0
42 // y -> 1
43 llvm::StringMap<size_t> ArgMap;
44
45 bool ObjectLike = true;
46 };
47
48 class MacroExpander::DefinitionParser {
49 public:
DefinitionParser(ArrayRef<FormatToken * > Tokens)50 DefinitionParser(ArrayRef<FormatToken *> Tokens) : Tokens(Tokens) {
51 assert(!Tokens.empty());
52 Current = Tokens[0];
53 }
54
55 // Parse the token stream and return the corresponding Definition object.
56 // Returns an empty definition object with a null-Name on error.
parse()57 MacroExpander::Definition parse() {
58 if (Current->isNot(tok::identifier))
59 return {};
60 Def.Name = Current->TokenText;
61 nextToken();
62 if (Current->is(tok::l_paren)) {
63 Def.ObjectLike = false;
64 if (!parseParams())
65 return {};
66 }
67 if (!parseExpansion())
68 return {};
69
70 return Def;
71 }
72
73 private:
parseParams()74 bool parseParams() {
75 assert(Current->is(tok::l_paren));
76 nextToken();
77 while (Current->is(tok::identifier)) {
78 Def.Params.push_back(Current);
79 Def.ArgMap[Def.Params.back()->TokenText] = Def.Params.size() - 1;
80 nextToken();
81 if (Current->isNot(tok::comma))
82 break;
83 nextToken();
84 }
85 if (Current->isNot(tok::r_paren))
86 return false;
87 nextToken();
88 return true;
89 }
90
parseExpansion()91 bool parseExpansion() {
92 if (!Current->isOneOf(tok::equal, tok::eof))
93 return false;
94 if (Current->is(tok::equal))
95 nextToken();
96 parseTail();
97 return true;
98 }
99
parseTail()100 void parseTail() {
101 while (Current->isNot(tok::eof)) {
102 Def.Body.push_back(Current);
103 nextToken();
104 }
105 Def.Body.push_back(Current);
106 }
107
nextToken()108 void nextToken() {
109 if (Pos + 1 < Tokens.size())
110 ++Pos;
111 Current = Tokens[Pos];
112 Current->Finalized = true;
113 }
114
115 size_t Pos = 0;
116 FormatToken *Current = nullptr;
117 Definition Def;
118 ArrayRef<FormatToken *> Tokens;
119 };
120
MacroExpander(const std::vector<std::string> & Macros,SourceManager & SourceMgr,const FormatStyle & Style,llvm::SpecificBumpPtrAllocator<FormatToken> & Allocator,IdentifierTable & IdentTable)121 MacroExpander::MacroExpander(
122 const std::vector<std::string> &Macros, SourceManager &SourceMgr,
123 const FormatStyle &Style,
124 llvm::SpecificBumpPtrAllocator<FormatToken> &Allocator,
125 IdentifierTable &IdentTable)
126 : SourceMgr(SourceMgr), Style(Style), Allocator(Allocator),
127 IdentTable(IdentTable) {
128 for (const std::string &Macro : Macros)
129 parseDefinition(Macro);
130 }
131
132 MacroExpander::~MacroExpander() = default;
133
parseDefinition(const std::string & Macro)134 void MacroExpander::parseDefinition(const std::string &Macro) {
135 Buffers.push_back(
136 llvm::MemoryBuffer::getMemBufferCopy(Macro, "<scratch space>"));
137 FileID FID = SourceMgr.createFileID(Buffers.back()->getMemBufferRef());
138 FormatTokenLexer Lex(SourceMgr, FID, 0, Style, encoding::Encoding_UTF8,
139 Allocator, IdentTable);
140 const auto Tokens = Lex.lex();
141 if (!Tokens.empty()) {
142 DefinitionParser Parser(Tokens);
143 auto Definition = Parser.parse();
144 if (Definition.ObjectLike) {
145 ObjectLike[Definition.Name] = std::move(Definition);
146 } else {
147 FunctionLike[Definition.Name][Definition.Params.size()] =
148 std::move(Definition);
149 }
150 }
151 }
152
defined(StringRef Name) const153 bool MacroExpander::defined(StringRef Name) const {
154 return FunctionLike.contains(Name) || ObjectLike.contains(Name);
155 }
156
objectLike(StringRef Name) const157 bool MacroExpander::objectLike(StringRef Name) const {
158 return ObjectLike.contains(Name);
159 }
160
hasArity(StringRef Name,unsigned Arity) const161 bool MacroExpander::hasArity(StringRef Name, unsigned Arity) const {
162 auto it = FunctionLike.find(Name);
163 return it != FunctionLike.end() && it->second.contains(Arity);
164 }
165
166 SmallVector<FormatToken *, 8>
expand(FormatToken * ID,std::optional<ArgsList> OptionalArgs) const167 MacroExpander::expand(FormatToken *ID,
168 std::optional<ArgsList> OptionalArgs) const {
169 if (OptionalArgs)
170 assert(hasArity(ID->TokenText, OptionalArgs->size()));
171 else
172 assert(objectLike(ID->TokenText));
173 const Definition &Def = OptionalArgs
174 ? FunctionLike.find(ID->TokenText)
175 ->second.find(OptionalArgs.value().size())
176 ->second
177 : ObjectLike.find(ID->TokenText)->second;
178 ArgsList Args = OptionalArgs ? OptionalArgs.value() : ArgsList();
179 SmallVector<FormatToken *, 8> Result;
180 // Expand each argument at most once.
181 llvm::StringSet<> ExpandedArgs;
182
183 // Adds the given token to Result.
184 auto pushToken = [&](FormatToken *Tok) {
185 Tok->MacroCtx->ExpandedFrom.push_back(ID);
186 Result.push_back(Tok);
187 };
188
189 // If Tok references a parameter, adds the corresponding argument to Result.
190 // Returns false if Tok does not reference a parameter.
191 auto expandArgument = [&](FormatToken *Tok) -> bool {
192 // If the current token references a parameter, expand the corresponding
193 // argument.
194 if (Tok->isNot(tok::identifier) || ExpandedArgs.contains(Tok->TokenText))
195 return false;
196 ExpandedArgs.insert(Tok->TokenText);
197 auto I = Def.ArgMap.find(Tok->TokenText);
198 if (I == Def.ArgMap.end())
199 return false;
200 // If there are fewer arguments than referenced parameters, treat the
201 // parameter as empty.
202 // FIXME: Potentially fully abort the expansion instead.
203 if (I->getValue() >= Args.size())
204 return true;
205 for (FormatToken *Arg : Args[I->getValue()]) {
206 // A token can be part of a macro argument at multiple levels.
207 // For example, with "ID(x) x":
208 // in ID(ID(x)), 'x' is expanded first as argument to the inner
209 // ID, then again as argument to the outer ID. We keep the macro
210 // role the token had from the inner expansion.
211 if (!Arg->MacroCtx)
212 Arg->MacroCtx = MacroExpansion(MR_ExpandedArg);
213 pushToken(Arg);
214 }
215 return true;
216 };
217
218 // Expand the definition into Result.
219 for (FormatToken *Tok : Def.Body) {
220 if (expandArgument(Tok))
221 continue;
222 // Create a copy of the tokens from the macro body, i.e. were not provided
223 // by user code.
224 FormatToken *New = new (Allocator.Allocate()) FormatToken;
225 New->copyFrom(*Tok);
226 assert(!New->MacroCtx);
227 // Tokens that are not part of the user code are not formatted.
228 New->MacroCtx = MacroExpansion(MR_Hidden);
229 pushToken(New);
230 }
231 assert(Result.size() >= 1 && Result.back()->is(tok::eof));
232 if (Result.size() > 1) {
233 ++Result[0]->MacroCtx->StartOfExpansion;
234 ++Result[Result.size() - 2]->MacroCtx->EndOfExpansion;
235 }
236 return Result;
237 }
238
239 } // namespace format
240 } // namespace clang
241