1 // Copyright (C) 2020-2023 Free Software Foundation, Inc.
3 // This file is part of GCC.
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
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
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/>.
19 #ifndef RUST_MACRO_EXPAND_H
20 #define RUST_MACRO_EXPAND_H
22 #include "rust-buffered-queue.h"
23 #include "rust-parse.h"
24 #include "rust-token.h"
26 #include "rust-macro.h"
27 #include "rust-hir-map.h"
28 #include "rust-name-resolver.h"
29 #include "rust-macro-invoc-lexer.h"
31 // Provides objects and method prototypes for macro expansion
34 // forward decls for AST
36 class MacroInvocation
;
39 // Object used to store configuration data for macro expansion.
40 // NOTE: Keep all these items complying with the latest rustc.
44 // TODO: Add `features' when we have it.
45 unsigned int recursion_limit
= 1024;
46 bool trace_mac
= false; // trace macro
47 bool should_test
= false; // strip #[test] nodes if false
48 bool keep_macs
= false; // keep macro definitions
49 std::string crate_name
= "";
52 struct MatchedFragment
54 std::string fragment_ident
;
55 size_t token_offset_begin
;
56 size_t token_offset_end
;
58 MatchedFragment (std::string identifier
, size_t token_offset_begin
,
59 size_t token_offset_end
)
60 : fragment_ident (identifier
), token_offset_begin (token_offset_begin
),
61 token_offset_end (token_offset_end
)
65 * Empty constructor for uninitialized fragments
67 MatchedFragment () : MatchedFragment ("", 0, 0) {}
69 std::string
as_string () const
71 return fragment_ident
+ "=" + std::to_string (token_offset_begin
) + ":"
72 + std::to_string (token_offset_end
);
76 class MatchedFragmentContainer
79 // Does the container refer to a simple metavariable, different from a
80 // repetition repeated once
87 MatchedFragmentContainer (std::vector
<MatchedFragment
> fragments
,
88 Kind kind
= Kind::Repetition
)
89 : fragments (fragments
), kind (kind
)
93 * Create a valid fragment matched zero times. This is useful for repetitions
94 * which allow the absence of a fragment, such as * and ?
96 static MatchedFragmentContainer
zero ()
98 return MatchedFragmentContainer ({});
102 * Create a valid fragment matched one time
104 static MatchedFragmentContainer
metavar (MatchedFragment fragment
)
106 return MatchedFragmentContainer ({fragment
}, Kind::MetaVar
);
110 * Add a matched fragment to the container
112 void add_fragment (MatchedFragment fragment
)
114 rust_assert (!is_single_fragment ());
116 fragments
.emplace_back (fragment
);
119 size_t get_match_amount () const { return fragments
.size (); }
120 const std::vector
<MatchedFragment
> &get_fragments () const
124 // const std::string &get_fragment_name () const { return fragment_name; }
126 bool is_single_fragment () const
128 return get_match_amount () == 1 && kind
== Kind::MetaVar
;
131 const MatchedFragment
get_single_fragment () const
133 rust_assert (is_single_fragment ());
138 const Kind
&get_kind () const { return kind
; }
142 * Fragments matched `match_amount` times. This can be an empty vector
143 * in case having zero matches is allowed (i.e ? or * operators)
145 std::vector
<MatchedFragment
> fragments
;
149 class SubstitutionScope
152 SubstitutionScope () : stack () {}
154 void push () { stack
.push_back ({}); }
156 std::map
<std::string
, MatchedFragmentContainer
> pop ()
158 auto top
= stack
.back ();
163 std::map
<std::string
, MatchedFragmentContainer
> &peek ()
165 return stack
.back ();
169 * Insert a new matched metavar into the current substitution map
171 void insert_metavar (MatchedFragment fragment
)
173 auto ¤t_map
= stack
.back ();
174 auto it
= current_map
.find (fragment
.fragment_ident
);
176 if (it
== current_map
.end ())
177 current_map
.insert ({fragment
.fragment_ident
,
178 MatchedFragmentContainer::metavar (fragment
)});
184 * Append a new matched fragment to a repetition into the current substitution
187 void append_fragment (MatchedFragment fragment
)
189 auto ¤t_map
= stack
.back ();
190 auto it
= current_map
.find (fragment
.fragment_ident
);
192 if (it
== current_map
.end ())
194 {fragment
.fragment_ident
, MatchedFragmentContainer ({fragment
})});
196 it
->second
.add_fragment (fragment
);
199 void insert_matches (std::string key
, MatchedFragmentContainer matches
)
201 auto ¤t_map
= stack
.back ();
202 auto it
= current_map
.find (key
);
203 rust_assert (it
== current_map
.end ());
205 current_map
.insert ({key
, matches
});
209 std::vector
<std::map
<std::string
, MatchedFragmentContainer
>> stack
;
212 // Object used to store shared data (between functions) for macro expansion.
227 unsigned int expansion_depth
= 0;
229 MacroExpander (AST::Crate
&crate
, ExpansionCfg cfg
, Session
&session
)
230 : cfg (cfg
), crate (crate
), session (session
),
231 sub_stack (SubstitutionScope ()),
232 expanded_fragment (AST::ASTFragment::create_error ()),
233 resolver (Resolver::Resolver::get ()),
234 mappings (Analysis::Mappings::get ())
237 ~MacroExpander () = default;
239 // Expands all macros in the crate passed in.
240 void expand_crate ();
242 /* Expands a macro invocation - possibly make both
243 * have similar duck-typed interface and use templates?*/
244 // should this be public or private?
245 void expand_invoc (AST::MacroInvocation
&invoc
, bool has_semicolon
);
247 // Expands a single declarative macro.
248 AST::ASTFragment
expand_decl_macro (Location locus
,
249 AST::MacroInvocData
&invoc
,
250 AST::MacroRulesDefinition
&rules_def
,
253 void expand_cfg_attrs (AST::AttrVec
&attrs
);
254 bool fails_cfg (const AST::AttrVec
&attr
) const;
255 bool fails_cfg_with_expand (AST::AttrVec
&attrs
) const;
257 bool depth_exceeds_recursion_limit () const;
259 bool try_match_rule (AST::MacroRule
&match_rule
,
260 AST::DelimTokenTree
&invoc_token_tree
);
262 AST::ASTFragment
transcribe_rule (
263 AST::MacroRule
&match_rule
, AST::DelimTokenTree
&invoc_token_tree
,
264 std::map
<std::string
, MatchedFragmentContainer
> &matched_fragments
,
265 bool semicolon
, ContextType ctx
);
267 bool match_fragment (Parser
<MacroInvocLexer
> &parser
,
268 AST::MacroMatchFragment
&fragment
);
270 bool match_token (Parser
<MacroInvocLexer
> &parser
, AST::Token
&token
);
272 bool match_repetition (Parser
<MacroInvocLexer
> &parser
,
273 AST::MacroMatchRepetition
&rep
);
275 bool match_matcher (Parser
<MacroInvocLexer
> &parser
,
276 AST::MacroMatcher
&matcher
);
279 * Match any amount of matches
281 * @param parser Parser to use for matching
282 * @param rep Repetition to try and match
283 * @param match_amount Reference in which to store the ammount of succesful
286 * @param lo_bound Lower bound of the matcher. When specified, the matcher
287 * will only succeed if it parses at *least* `lo_bound` fragments. If
288 * unspecified, the matcher could succeed when parsing 0 fragments.
290 * @param hi_bound Higher bound of the matcher. When specified, the matcher
291 * will only succeed if it parses *less than* `hi_bound` fragments. If
292 * unspecified, the matcher could succeed when parsing an infinity of
295 * @return true if matching was successful and within the given limits, false
298 bool match_n_matches (Parser
<MacroInvocLexer
> &parser
,
299 AST::MacroMatchRepetition
&rep
, size_t &match_amount
,
300 size_t lo_bound
= 0, size_t hi_bound
= 0);
302 void push_context (ContextType t
) { context
.push_back (t
); }
304 ContextType
pop_context ()
306 rust_assert (!context
.empty ());
308 ContextType t
= context
.back ();
314 ContextType
peek_context () { return context
.back (); }
316 void set_expanded_fragment (AST::ASTFragment
&&fragment
)
318 expanded_fragment
= std::move (fragment
);
321 AST::ASTFragment
take_expanded_fragment (AST::ASTVisitor
&vis
)
323 AST::ASTFragment old_fragment
= std::move (expanded_fragment
);
324 auto accumulator
= std::vector
<AST::SingleASTNode
> ();
325 expanded_fragment
= AST::ASTFragment::create_error ();
327 for (auto &node
: old_fragment
.get_nodes ())
330 node
.accept_vis (vis
);
331 // we'll decide the next move according to the outcome of the macro
333 if (expanded_fragment
.is_error ())
334 accumulator
.push_back (node
); // if expansion fails, there might be a
335 // non-macro expression we need to keep
338 // if expansion succeeded, then we need to merge the fragment with
339 // the contents in the accumulator, so that our final expansion
340 // result will contain non-macro nodes as it should
341 auto new_nodes
= expanded_fragment
.get_nodes ();
342 std::move (new_nodes
.begin (), new_nodes
.end (),
343 std::back_inserter (accumulator
));
344 expanded_fragment
= AST::ASTFragment (accumulator
);
355 SubstitutionScope sub_stack
;
356 std::vector
<ContextType
> context
;
357 AST::ASTFragment expanded_fragment
;
360 Resolver::Resolver
*resolver
;
361 Analysis::Mappings
*mappings
;