| // Copyright (C) 2020-2023 Free Software Foundation, Inc. |
| |
| // This file is part of GCC. |
| |
| // GCC is free software; you can redistribute it and/or modify it under |
| // the terms of the GNU General Public License as published by the Free |
| // Software Foundation; either version 3, or (at your option) any later |
| // version. |
| |
| // GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| // WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| // for more details. |
| |
| // You should have received a copy of the GNU General Public License |
| // along with GCC; see the file COPYING3. If not see |
| // <http://www.gnu.org/licenses/>. |
| |
| #ifndef RUST_MACRO_EXPAND_H |
| #define RUST_MACRO_EXPAND_H |
| |
| #include "rust-buffered-queue.h" |
| #include "rust-parse.h" |
| #include "rust-token.h" |
| #include "rust-ast.h" |
| #include "rust-macro.h" |
| #include "rust-hir-map.h" |
| #include "rust-early-name-resolver.h" |
| #include "rust-name-resolver.h" |
| #include "rust-macro-invoc-lexer.h" |
| |
| // Provides objects and method prototypes for macro expansion |
| |
| namespace Rust { |
| // forward decls for AST |
| namespace AST { |
| class MacroInvocation; |
| } |
| |
| // Object used to store configuration data for macro expansion. |
| // NOTE: Keep all these items complying with the latest rustc. |
| struct ExpansionCfg |
| { |
| // features? |
| // TODO: Add `features' when we have it. |
| unsigned int recursion_limit = 1024; |
| bool trace_mac = false; // trace macro |
| bool should_test = false; // strip #[test] nodes if false |
| bool keep_macs = false; // keep macro definitions |
| std::string crate_name = ""; |
| }; |
| |
| struct MatchedFragment |
| { |
| std::string fragment_ident; |
| size_t token_offset_begin; |
| size_t token_offset_end; |
| |
| MatchedFragment (std::string identifier, size_t token_offset_begin, |
| size_t token_offset_end) |
| : fragment_ident (identifier), token_offset_begin (token_offset_begin), |
| token_offset_end (token_offset_end) |
| {} |
| |
| /** |
| * Empty constructor for uninitialized fragments |
| */ |
| MatchedFragment () : MatchedFragment ("", 0, 0) {} |
| |
| std::string as_string () const |
| { |
| return fragment_ident + "=" + std::to_string (token_offset_begin) + ":" |
| + std::to_string (token_offset_end); |
| } |
| }; |
| |
| class MatchedFragmentContainer |
| { |
| public: |
| // Does the container refer to a simple metavariable, different from a |
| // repetition repeated once |
| enum class Kind |
| { |
| MetaVar, |
| Repetition, |
| }; |
| |
| MatchedFragmentContainer (std::vector<MatchedFragment> fragments, |
| Kind kind = Kind::Repetition) |
| : fragments (fragments), kind (kind) |
| {} |
| |
| /** |
| * Create a valid fragment matched zero times. This is useful for repetitions |
| * which allow the absence of a fragment, such as * and ? |
| */ |
| static MatchedFragmentContainer zero () |
| { |
| return MatchedFragmentContainer ({}); |
| } |
| |
| /** |
| * Create a valid fragment matched one time |
| */ |
| static MatchedFragmentContainer metavar (MatchedFragment fragment) |
| { |
| return MatchedFragmentContainer ({fragment}, Kind::MetaVar); |
| } |
| |
| /** |
| * Add a matched fragment to the container |
| */ |
| void add_fragment (MatchedFragment fragment) |
| { |
| rust_assert (!is_single_fragment ()); |
| |
| fragments.emplace_back (fragment); |
| } |
| |
| size_t get_match_amount () const { return fragments.size (); } |
| const std::vector<MatchedFragment> &get_fragments () const |
| { |
| return fragments; |
| } |
| // const std::string &get_fragment_name () const { return fragment_name; } |
| |
| bool is_single_fragment () const |
| { |
| return get_match_amount () == 1 && kind == Kind::MetaVar; |
| } |
| |
| const MatchedFragment get_single_fragment () const |
| { |
| rust_assert (is_single_fragment ()); |
| |
| return fragments[0]; |
| } |
| |
| const Kind &get_kind () const { return kind; } |
| |
| private: |
| /** |
| * Fragments matched `match_amount` times. This can be an empty vector |
| * in case having zero matches is allowed (i.e ? or * operators) |
| */ |
| std::vector<MatchedFragment> fragments; |
| Kind kind; |
| }; |
| |
| class SubstitutionScope |
| { |
| public: |
| SubstitutionScope () : stack () {} |
| |
| void push () { stack.push_back ({}); } |
| |
| std::map<std::string, MatchedFragmentContainer> pop () |
| { |
| auto top = stack.back (); |
| stack.pop_back (); |
| return top; |
| } |
| |
| std::map<std::string, MatchedFragmentContainer> &peek () |
| { |
| return stack.back (); |
| } |
| |
| /** |
| * Insert a new matched metavar into the current substitution map |
| */ |
| void insert_metavar (MatchedFragment fragment) |
| { |
| auto ¤t_map = stack.back (); |
| auto it = current_map.find (fragment.fragment_ident); |
| |
| if (it == current_map.end ()) |
| current_map.insert ({fragment.fragment_ident, |
| MatchedFragmentContainer::metavar (fragment)}); |
| else |
| gcc_unreachable (); |
| } |
| |
| /** |
| * Append a new matched fragment to a repetition into the current substitution |
| * map |
| */ |
| void append_fragment (MatchedFragment fragment) |
| { |
| auto ¤t_map = stack.back (); |
| auto it = current_map.find (fragment.fragment_ident); |
| |
| if (it == current_map.end ()) |
| current_map.insert ( |
| {fragment.fragment_ident, MatchedFragmentContainer ({fragment})}); |
| else |
| it->second.add_fragment (fragment); |
| } |
| |
| void insert_matches (std::string key, MatchedFragmentContainer matches) |
| { |
| auto ¤t_map = stack.back (); |
| auto it = current_map.find (key); |
| rust_assert (it == current_map.end ()); |
| |
| current_map.insert ({key, matches}); |
| } |
| |
| private: |
| std::vector<std::map<std::string, MatchedFragmentContainer>> stack; |
| }; |
| |
| // Object used to store shared data (between functions) for macro expansion. |
| struct MacroExpander |
| { |
| enum ContextType |
| { |
| ITEM, |
| BLOCK, |
| EXTERN, |
| TYPE, |
| TRAIT, |
| IMPL, |
| TRAIT_IMPL, |
| }; |
| |
| ExpansionCfg cfg; |
| unsigned int expansion_depth = 0; |
| |
| MacroExpander (AST::Crate &crate, ExpansionCfg cfg, Session &session) |
| : cfg (cfg), crate (crate), session (session), |
| sub_stack (SubstitutionScope ()), |
| expanded_fragment (AST::Fragment::create_error ()), |
| has_changed_flag (false), resolver (Resolver::Resolver::get ()), |
| mappings (Analysis::Mappings::get ()) |
| {} |
| |
| ~MacroExpander () = default; |
| |
| // Expands all macros in the crate passed in. |
| void expand_crate (); |
| |
| /** |
| * Expand the eager invocations contained within a builtin macro invocation. |
| * Called by `expand_invoc` when expanding builtin invocations. |
| */ |
| void expand_eager_invocations (AST::MacroInvocation &invoc); |
| |
| /* Expands a macro invocation - possibly make both |
| * have similar duck-typed interface and use templates?*/ |
| // should this be public or private? |
| void expand_invoc (AST::MacroInvocation &invoc, bool has_semicolon); |
| |
| // Expands a single declarative macro. |
| AST::Fragment expand_decl_macro (Location locus, AST::MacroInvocData &invoc, |
| AST::MacroRulesDefinition &rules_def, |
| bool semicolon); |
| |
| void expand_cfg_attrs (AST::AttrVec &attrs); |
| bool fails_cfg (const AST::AttrVec &attr) const; |
| bool fails_cfg_with_expand (AST::AttrVec &attrs) const; |
| |
| bool depth_exceeds_recursion_limit () const; |
| |
| bool try_match_rule (AST::MacroRule &match_rule, |
| AST::DelimTokenTree &invoc_token_tree); |
| |
| AST::Fragment transcribe_rule ( |
| AST::MacroRule &match_rule, AST::DelimTokenTree &invoc_token_tree, |
| std::map<std::string, MatchedFragmentContainer> &matched_fragments, |
| bool semicolon, ContextType ctx); |
| |
| bool match_fragment (Parser<MacroInvocLexer> &parser, |
| AST::MacroMatchFragment &fragment); |
| |
| bool match_token (Parser<MacroInvocLexer> &parser, AST::Token &token); |
| |
| bool match_repetition (Parser<MacroInvocLexer> &parser, |
| AST::MacroMatchRepetition &rep); |
| |
| bool match_matcher (Parser<MacroInvocLexer> &parser, |
| AST::MacroMatcher &matcher, bool in_repetition = false); |
| |
| /** |
| * Match any amount of matches |
| * |
| * @param parser Parser to use for matching |
| * @param rep Repetition to try and match |
| * @param match_amount Reference in which to store the ammount of succesful |
| * and valid matches |
| * |
| * @param lo_bound Lower bound of the matcher. When specified, the matcher |
| * will only succeed if it parses at *least* `lo_bound` fragments. If |
| * unspecified, the matcher could succeed when parsing 0 fragments. |
| * |
| * @param hi_bound Higher bound of the matcher. When specified, the matcher |
| * will only succeed if it parses *less than* `hi_bound` fragments. If |
| * unspecified, the matcher could succeed when parsing an infinity of |
| * fragments. |
| * |
| * @return true if matching was successful and within the given limits, false |
| * otherwise |
| */ |
| bool match_n_matches (Parser<MacroInvocLexer> &parser, |
| AST::MacroMatchRepetition &rep, size_t &match_amount, |
| size_t lo_bound = 0, size_t hi_bound = 0); |
| |
| void push_context (ContextType t) { context.push_back (t); } |
| |
| ContextType pop_context () |
| { |
| rust_assert (!context.empty ()); |
| |
| ContextType t = context.back (); |
| context.pop_back (); |
| |
| return t; |
| } |
| |
| ContextType peek_context () { return context.back (); } |
| |
| void set_expanded_fragment (AST::Fragment &&fragment) |
| { |
| if (!fragment.is_error ()) |
| has_changed_flag = true; |
| |
| expanded_fragment = std::move (fragment); |
| } |
| |
| AST::Fragment take_expanded_fragment () |
| { |
| auto fragment = std::move (expanded_fragment); |
| expanded_fragment = AST::Fragment::create_error (); |
| |
| return fragment; |
| } |
| |
| /** |
| * Has the MacroExpander expanded a macro since its state was last reset? |
| */ |
| bool has_changed () const { return has_changed_flag; } |
| |
| /** |
| * Reset the expander's "changed" state. This function should be executed at |
| * each iteration in a fixed point loop |
| */ |
| void reset_changed_state () { has_changed_flag = false; } |
| |
| AST::MacroRulesDefinition *get_last_definition () { return last_def; } |
| AST::MacroInvocation *get_last_invocation () { return last_invoc; } |
| |
| private: |
| AST::Crate &crate; |
| Session &session; |
| SubstitutionScope sub_stack; |
| std::vector<ContextType> context; |
| AST::Fragment expanded_fragment; |
| bool has_changed_flag; |
| |
| AST::MacroRulesDefinition *last_def; |
| AST::MacroInvocation *last_invoc; |
| |
| public: |
| Resolver::Resolver *resolver; |
| Analysis::Mappings *mappings; |
| }; |
| |
| } // namespace Rust |
| |
| #endif |