Branch data Line data Source code
1 : : // Copyright (C) 2020-2024 Free Software Foundation, Inc.
2 : :
3 : : // This file is part of GCC.
4 : :
5 : : // GCC is free software; you can redistribute it and/or modify it under
6 : : // the terms of the GNU General Public License as published by the Free
7 : : // Software Foundation; either version 3, or (at your option) any later
8 : : // version.
9 : :
10 : : // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
11 : : // WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 : : // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 : : // for more details.
14 : :
15 : : // You should have received a copy of the GNU General Public License
16 : : // along with GCC; see the file COPYING3. If not see
17 : : // <http://www.gnu.org/licenses/>.
18 : :
19 : : #include "rust-macro-substitute-ctx.h"
20 : :
21 : : namespace Rust {
22 : :
23 : : bool
24 : 6482 : SubstituteCtx::substitute_metavar (
25 : : std::unique_ptr<AST::Token> &metavar,
26 : : std::vector<std::unique_ptr<AST::Token>> &expanded)
27 : : {
28 : 6482 : auto metavar_name = metavar->get_str ();
29 : :
30 : 6482 : auto it = fragments.find (metavar_name);
31 : 6482 : if (it == fragments.end ())
32 : : {
33 : : // fail to substitute
34 : :
35 : : // HACK: substitute ($ crate) => (crate)
36 : 1 : if (metavar->get_id () != CRATE)
37 : : return false;
38 : :
39 : 0 : expanded.push_back (metavar->clone_token ());
40 : 0 : return true;
41 : : }
42 : : else
43 : : {
44 : : // If we are expanding a metavar which has a lof of matches, we are
45 : : // currently expanding a repetition metavar - not a simple metavar. We
46 : : // need to error out and inform the user.
47 : : // Associated test case for an example: compile/macro-issue1224.rs
48 : 6481 : if (!it->second->is_single_fragment ())
49 : : {
50 : 1 : rust_error_at (metavar->get_locus (),
51 : : "metavariable is still repeating at this depth");
52 : 1 : rust_inform (
53 : : metavar->get_locus (),
54 : : "you probably forgot the repetition operator: %<%s%s%s%>", "$(",
55 : 1 : metavar->as_string ().c_str (), ")*");
56 : 1 : return true;
57 : : }
58 : :
59 : : // We only care about the vector when expanding repetitions.
60 : : // Just access the first element of the vector.
61 : 6480 : auto &frag = it->second->get_single_fragment ();
62 : 15004 : for (size_t offs = frag.token_offset_begin; offs < frag.token_offset_end;
63 : : offs++)
64 : : {
65 : 8524 : auto &tok = input.at (offs);
66 : 8524 : expanded.push_back (tok->clone_token ());
67 : : }
68 : : }
69 : :
70 : : return true;
71 : 6482 : }
72 : :
73 : : bool
74 : 1481 : SubstituteCtx::check_repetition_amount (size_t pattern_start,
75 : : size_t pattern_end,
76 : : size_t &expected_repetition_amount)
77 : : {
78 : 1481 : bool first_fragment_found = false;
79 : 1481 : bool is_valid = true;
80 : :
81 : 12388 : for (size_t i = pattern_start; i < pattern_end; i++)
82 : : {
83 : 10907 : if (macro.at (i)->get_id () == DOLLAR_SIGN)
84 : : {
85 : 1604 : auto &frag_token = macro.at (i + 1);
86 : 1604 : if (token_id_is_keyword (frag_token->get_id ())
87 : 1604 : || frag_token->get_id () == IDENTIFIER)
88 : : {
89 : 1602 : auto it = fragments.find (frag_token->get_str ());
90 : 1602 : if (it == fragments.end ())
91 : : {
92 : : // If the repetition is not anything we know (ie no declared
93 : : // metavars, or metavars which aren't present in the
94 : : // fragment), we can just error out. No need to paste the
95 : : // tokens as if nothing had happened.
96 : 0 : rust_error_at (frag_token->get_locus (),
97 : : "metavar %s used in repetition does not exist",
98 : 0 : frag_token->get_str ().c_str ());
99 : :
100 : 0 : is_valid = false;
101 : : }
102 : :
103 : 1602 : auto &fragment = *it->second;
104 : :
105 : 1602 : if (!fragment.is_single_fragment ())
106 : : {
107 : 1600 : auto &fragment_rep
108 : : = static_cast<MatchedFragmentContainerRepetition &> (
109 : : fragment);
110 : 1600 : size_t repeat_amount = fragment_rep.get_match_amount ();
111 : 1600 : if (!first_fragment_found)
112 : : {
113 : 1481 : first_fragment_found = true;
114 : 1481 : expected_repetition_amount = repeat_amount;
115 : : }
116 : : else
117 : : {
118 : 119 : if (repeat_amount != expected_repetition_amount)
119 : : {
120 : 2 : rust_error_at (
121 : : frag_token->get_locus (),
122 : : "different amount of matches used in merged "
123 : : "repetitions: expected %lu, got %lu",
124 : : (unsigned long) expected_repetition_amount,
125 : : (unsigned long) repeat_amount);
126 : 2 : is_valid = false;
127 : : }
128 : : }
129 : : }
130 : : }
131 : : }
132 : : }
133 : :
134 : 1481 : return is_valid && first_fragment_found;
135 : : }
136 : :
137 : : std::vector<std::unique_ptr<AST::Token>>
138 : 1481 : SubstituteCtx::substitute_repetition (
139 : : size_t pattern_start, size_t pattern_end,
140 : : std::unique_ptr<AST::Token> separator_token)
141 : : {
142 : 1481 : rust_assert (pattern_end < macro.size ());
143 : :
144 : 1481 : size_t repeat_amount = 0;
145 : 1481 : if (!check_repetition_amount (pattern_start, pattern_end, repeat_amount))
146 : 2 : return {};
147 : :
148 : 1479 : rust_debug ("repetition amount to use: %lu", (unsigned long) repeat_amount);
149 : 1479 : std::vector<std::unique_ptr<AST::Token>> expanded;
150 : 1479 : std::vector<std::unique_ptr<AST::Token>> new_macro;
151 : :
152 : : // We want to generate a "new macro" to substitute with. This new macro
153 : : // should contain only the tokens inside the pattern
154 : 12362 : for (size_t tok_idx = pattern_start; tok_idx < pattern_end; tok_idx++)
155 : 10883 : new_macro.emplace_back (macro.at (tok_idx)->clone_token ());
156 : :
157 : : // Then, we want to create a subset of the matches so that
158 : : // `substitute_tokens()` can only see one fragment per metavar. Let's say we
159 : : // have the following user input: (1 145 'h')
160 : : // on the following match arm: ($($lit:literal)*)
161 : : // which causes the following matches: { "lit": [1, 145, 'h'] }
162 : : //
163 : : // The pattern (new_macro) is `$lit:literal`
164 : : // The first time we expand it, we want $lit to have the following token: 1
165 : : // The second time, 145
166 : : // The third and final time, 'h'
167 : : //
168 : : // In order to do so we must create "sub maps", which only contain parts of
169 : : // the original matches
170 : : // sub-maps: [ { "lit": 1 }, { "lit": 145 }, { "lit": 'h' } ]
171 : : //
172 : : // and give them to `substitute_tokens` one by one.
173 : :
174 : 4594 : for (size_t i = 0; i < repeat_amount; i++)
175 : : {
176 : 3115 : std::map<std::string, MatchedFragmentContainer *> sub_map;
177 : 7881 : for (auto &kv_match : fragments)
178 : : {
179 : 4766 : if (kv_match.second->is_single_fragment ())
180 : 1531 : sub_map.emplace (kv_match.first, kv_match.second);
181 : : // Hack: A repeating meta variable might not be present in the new
182 : : // macro. Don't include this match if the fragment doesn't have enough
183 : : // items, as check_repetition_amount should prevent repetition amount
184 : : // mismatches anyway.
185 : 3235 : else if (kv_match.second->get_fragments ().size () > i)
186 : 3220 : sub_map.emplace (kv_match.first,
187 : 3220 : kv_match.second->get_fragments ().at (i).get ());
188 : : }
189 : :
190 : 3115 : auto substitute_context = SubstituteCtx (input, new_macro, sub_map);
191 : 3115 : auto new_tokens = substitute_context.substitute_tokens ();
192 : :
193 : : // Skip the first repetition, but add the separator to the expanded
194 : : // tokens if it is present
195 : 3115 : if (i != 0 && separator_token)
196 : 155 : expanded.emplace_back (separator_token->clone_token ());
197 : :
198 : 48680 : for (auto &new_token : new_tokens)
199 : 45565 : expanded.emplace_back (new_token->clone_token ());
200 : 3115 : }
201 : :
202 : : // FIXME: We also need to make sure that all subsequent fragments
203 : : // contain the same amount of repetitions as the first one
204 : :
205 : 1479 : return expanded;
206 : 1479 : }
207 : :
208 : : static bool
209 : 1481 : is_rep_op (std::unique_ptr<AST::Token> &tok)
210 : : {
211 : 1481 : auto id = tok->get_id ();
212 : 1481 : return id == QUESTION_MARK || id == ASTERISK || id == PLUS;
213 : : }
214 : :
215 : : std::pair<std::vector<std::unique_ptr<AST::Token>>, size_t>
216 : 7964 : SubstituteCtx::substitute_token (size_t token_idx)
217 : : {
218 : 7964 : auto &token = macro.at (token_idx);
219 : :
220 : 7964 : switch (token->get_id ())
221 : : {
222 : 2 : default:
223 : 2 : if (token_id_is_keyword (token->get_id ()))
224 : : {
225 : 6482 : case IDENTIFIER:
226 : 6483 : std::vector<std::unique_ptr<AST::Token>> expanded;
227 : :
228 : 6482 : rust_debug ("expanding metavar: %s", token->get_str ().c_str ());
229 : :
230 : 6482 : if (substitute_metavar (token, expanded))
231 : 6481 : return {std::move (expanded), 2};
232 : : }
233 : :
234 : : // don't substitute, dollar sign is alone/metavar is unknown
235 : 2 : return {std::vector<std::unique_ptr<AST::Token>> (), 0};
236 : :
237 : 1481 : case LEFT_PAREN: {
238 : : // We need to parse up until the closing delimiter and expand this
239 : : // fragment->n times.
240 : 1481 : rust_debug ("expanding repetition");
241 : :
242 : : // We're in a context where macro repetitions have already been
243 : : // parsed and validated: This means that
244 : : // 1/ There will be no delimiters as that is an error
245 : : // 2/ There are no fragment specifiers anymore, which prevents us
246 : : // from reusing parser functions.
247 : : //
248 : : // Repetition patterns are also special in that they cannot contain
249 : : // "rogue" delimiters: For example, this is invalid, as they are
250 : : // parsed as MacroMatches and must contain a correct amount of
251 : : // delimiters.
252 : : // `$($e:expr ) )`
253 : : // ^ rogue closing parenthesis
254 : : //
255 : : // With all of that in mind, we can simply skip ahead from one
256 : : // parenthesis to the other to find the pattern to expand. Of course,
257 : : // pairs of delimiters, including parentheses, are allowed.
258 : : // `$($e:expr ( ) )`
259 : : // Parentheses are the sole delimiter for which we need a special
260 : : // behavior since they delimit the repetition pattern
261 : :
262 : 1481 : size_t pattern_start = token_idx + 1;
263 : 1481 : size_t pattern_end = pattern_start;
264 : 1481 : auto parentheses_stack = 0;
265 : 12388 : for (size_t idx = pattern_start; idx < macro.size (); idx++)
266 : : {
267 : 12388 : if (macro.at (idx)->get_id () == LEFT_PAREN)
268 : : {
269 : 528 : parentheses_stack++;
270 : : }
271 : 11860 : else if (macro.at (idx)->get_id () == RIGHT_PAREN)
272 : : {
273 : 2009 : if (parentheses_stack == 0)
274 : : {
275 : : pattern_end = idx;
276 : : break;
277 : : }
278 : 528 : parentheses_stack--;
279 : : }
280 : : }
281 : :
282 : : // Unreachable case, but let's make sure we don't ever run into it
283 : 1481 : rust_assert (pattern_end != pattern_start);
284 : :
285 : 1481 : std::unique_ptr<AST::Token> separator_token = nullptr;
286 : 1481 : if (pattern_end + 1 <= macro.size ())
287 : : {
288 : 1481 : auto &post_pattern_token = macro.at (pattern_end + 1);
289 : 1481 : if (!is_rep_op (post_pattern_token))
290 : 196 : separator_token = post_pattern_token->clone_token ();
291 : : }
292 : :
293 : : // Amount of tokens to skip
294 : 1481 : auto to_skip = 0;
295 : : // Parentheses
296 : 1481 : to_skip += 2;
297 : : // Repetition operator
298 : 1481 : to_skip += 1;
299 : : // Separator
300 : 1481 : if (separator_token)
301 : 196 : to_skip += 1;
302 : :
303 : 1481 : return {substitute_repetition (pattern_start, pattern_end,
304 : : std::move (separator_token)),
305 : 1481 : pattern_end - pattern_start + to_skip + 1};
306 : 1481 : }
307 : : }
308 : :
309 : : rust_unreachable ();
310 : : }
311 : :
312 : : std::vector<std::unique_ptr<AST::Token>>
313 : 5161 : SubstituteCtx::substitute_tokens ()
314 : : {
315 : 5161 : std::vector<std::unique_ptr<AST::Token>> replaced_tokens;
316 : 5161 : rust_debug ("expanding tokens");
317 : :
318 : 72854 : for (size_t i = 0; i < macro.size ();)
319 : : {
320 : 67693 : auto &tok = macro.at (i);
321 : 67693 : if (tok->get_id () == DOLLAR_SIGN)
322 : : {
323 : : // Aaaaah, if only we had C++17 :)
324 : : // auto [expanded, tok_to_skip] = ...
325 : 7964 : auto p = substitute_token (i + 1);
326 : 7964 : auto expanded = std::move (p.first);
327 : 7964 : auto tok_to_skip = p.second;
328 : :
329 : 7964 : if (!tok_to_skip)
330 : : {
331 : 2 : replaced_tokens.emplace_back (tok->clone_token ());
332 : 2 : tok_to_skip++;
333 : : }
334 : :
335 : 7964 : i += tok_to_skip;
336 : :
337 : 62208 : for (auto &token : expanded)
338 : 54244 : replaced_tokens.emplace_back (token->clone_token ());
339 : 7964 : }
340 : : else
341 : : {
342 : 59729 : replaced_tokens.emplace_back (tok->clone_token ());
343 : 59729 : i++;
344 : : }
345 : : }
346 : :
347 : 5161 : return replaced_tokens;
348 : : }
349 : :
350 : : } // namespace Rust
|