LCOV - code coverage report
Current view: top level - gcc/rust/expand - rust-macro-expand.h (source / functions) Coverage Total Hit
Test: gcc.info Lines: 71.4 % 119 85
Test Date: 2026-02-28 14:20:25 Functions: 54.5 % 22 12
Legend: Lines:     hit not hit

            Line data    Source code
       1              : // Copyright (C) 2020-2026 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              : #ifndef RUST_MACRO_EXPAND_H
      20              : #define RUST_MACRO_EXPAND_H
      21              : 
      22              : #include "optional.h"
      23              : #include "rust-ast-fragment.h"
      24              : #include "rust-buffered-queue.h"
      25              : #include "rust-parse.h"
      26              : #include "rust-token.h"
      27              : #include "rust-ast.h"
      28              : #include "rust-macro.h"
      29              : #include "rust-hir-map.h"
      30              : #include "rust-name-resolver.h"
      31              : #include "rust-macro-invoc-lexer.h"
      32              : #include "rust-token-converter.h"
      33              : #include "rust-ast-collector.h"
      34              : #include "rust-system.h"
      35              : #include "libproc_macro_internal/proc_macro.h"
      36              : 
      37              : // Provides objects and method prototypes for macro expansion
      38              : 
      39              : namespace Rust {
      40              : // forward decls for AST
      41              : namespace AST {
      42              : class MacroInvocation;
      43              : }
      44              : 
      45              : // Object used to store configuration data for macro expansion.
      46              : // NOTE: Keep all these items complying with the latest rustc.
      47         9020 : struct ExpansionCfg
      48              : {
      49              :   // features?
      50              :   // TODO: Add `features' when we have it.
      51              :   unsigned int recursion_limit = 1024;
      52              :   bool trace_mac = false;   // trace macro
      53              :   bool should_test = false; // strip #[test] nodes if false
      54              :   bool keep_macs = false;   // keep macro definitions
      55              :   std::string crate_name = "";
      56              : };
      57              : 
      58         8151 : struct MatchedFragment
      59              : {
      60              :   std::string fragment_ident;
      61              :   size_t token_offset_begin;
      62              :   size_t token_offset_end;
      63              : 
      64         8151 :   MatchedFragment (std::string identifier, size_t token_offset_begin,
      65              :                    size_t token_offset_end)
      66         8151 :     : fragment_ident (identifier), token_offset_begin (token_offset_begin),
      67         8151 :       token_offset_end (token_offset_end)
      68              :   {}
      69              : 
      70              :   /**
      71              :    * Empty constructor for uninitialized fragments
      72              :    */
      73              :   MatchedFragment () : MatchedFragment ("", 0, 0) {}
      74              : 
      75            0 :   std::string as_string () const
      76              :   {
      77            0 :     return fragment_ident + "=" + std::to_string (token_offset_begin) + ":"
      78            0 :            + std::to_string (token_offset_end);
      79              :   }
      80              : };
      81              : 
      82         9920 : class MatchedFragmentContainer
      83              : {
      84              : public:
      85              :   // Does the container refer to a simple metavariable, different from a
      86              :   // repetition repeated once
      87              :   enum class Kind
      88              :   {
      89              :     MetaVar,
      90              :     Repetition,
      91              :   };
      92              : 
      93              :   virtual ~MatchedFragmentContainer () = default;
      94              : 
      95              :   virtual Kind get_kind () const = 0;
      96              : 
      97              :   virtual std::string as_string () const = 0;
      98              : 
      99              :   /**
     100              :    * Create a valid fragment matched zero times. This is useful for repetitions
     101              :    * which allow the absence of a fragment, such as * and ?
     102              :    */
     103              :   static std::unique_ptr<MatchedFragmentContainer> zero ();
     104              : 
     105              :   /**
     106              :    * Create a valid fragment matched one time
     107              :    */
     108              :   static std::unique_ptr<MatchedFragmentContainer>
     109              :   metavar (MatchedFragment fragment);
     110              : 
     111              :   /**
     112              :    * Add a matched fragment to the container
     113              :    */
     114              :   void add_fragment (MatchedFragment fragment);
     115              : 
     116              :   /**
     117              :    * Add a matched fragment to the container
     118              :    */
     119              :   void add_fragment (std::unique_ptr<MatchedFragmentContainer> fragment);
     120              : 
     121              :   // const std::string &get_fragment_name () const { return fragment_name; }
     122              : 
     123        44151 :   bool is_single_fragment () const { return get_kind () == Kind::MetaVar; }
     124              : 
     125              :   MatchedFragment &get_single_fragment ();
     126              : 
     127              :   std::vector<std::unique_ptr<MatchedFragmentContainer>> &get_fragments ();
     128              : };
     129              : 
     130              : class MatchedFragmentContainerMetaVar : public MatchedFragmentContainer
     131              : {
     132              :   MatchedFragment fragment;
     133              : 
     134              : public:
     135         8151 :   MatchedFragmentContainerMetaVar (const MatchedFragment &fragment)
     136         8151 :     : fragment (fragment)
     137              :   {}
     138              : 
     139        11366 :   MatchedFragment &get_fragment () { return fragment; }
     140              : 
     141        24394 :   virtual Kind get_kind () const { return Kind::MetaVar; }
     142              : 
     143            0 :   virtual std::string as_string () const { return fragment.as_string (); }
     144              : };
     145              : 
     146              : class MatchedFragmentContainerRepetition : public MatchedFragmentContainer
     147              : {
     148              :   std::vector<std::unique_ptr<MatchedFragmentContainer>> fragments;
     149              : 
     150              : public:
     151         1769 :   MatchedFragmentContainerRepetition () {}
     152              : 
     153         2014 :   size_t get_match_amount () const { return fragments.size (); }
     154              : 
     155         8862 :   std::vector<std::unique_ptr<MatchedFragmentContainer>> &get_fragments ()
     156              :   {
     157         8862 :     return fragments;
     158              :   }
     159              : 
     160              :   /**
     161              :    * Add a matched fragment to the container
     162              :    */
     163            0 :   void add_fragment (MatchedFragment fragment)
     164              :   {
     165            0 :     add_fragment (metavar (fragment));
     166            0 :   }
     167              : 
     168              :   /**
     169              :    * Add a matched fragment to the container
     170              :    */
     171         4440 :   void add_fragment (std::unique_ptr<MatchedFragmentContainer> fragment)
     172              :   {
     173         4440 :     fragments.emplace_back (std::move (fragment));
     174              :   }
     175              : 
     176        19757 :   virtual Kind get_kind () const { return Kind::Repetition; }
     177              : 
     178            0 :   virtual std::string as_string () const
     179              :   {
     180            0 :     std::string acc = "[";
     181            0 :     for (size_t i = 0; i < fragments.size (); i++)
     182              :       {
     183            0 :         if (i)
     184            0 :           acc += " ";
     185            0 :         acc += fragments[i]->as_string ();
     186              :       }
     187            0 :     acc += "]";
     188            0 :     return acc;
     189              :   }
     190              : };
     191              : 
     192         9020 : class SubstitutionScope
     193              : {
     194              : public:
     195         4510 :   SubstitutionScope () : stack () {}
     196              : 
     197         8055 :   void push () { stack.push_back ({}); }
     198              : 
     199         8055 :   std::map<std::string, std::unique_ptr<MatchedFragmentContainer>> pop ()
     200              :   {
     201         8055 :     auto top = std::move (stack.back ());
     202         8055 :     stack.pop_back ();
     203         8055 :     return top;
     204              :   }
     205              : 
     206         1769 :   std::map<std::string, std::unique_ptr<MatchedFragmentContainer>> &peek ()
     207              :   {
     208         1769 :     return stack.back ();
     209              :   }
     210              : 
     211              :   /**
     212              :    * Insert a new matched metavar into the current substitution map
     213              :    */
     214         8151 :   void insert_metavar (MatchedFragment fragment)
     215              :   {
     216         8151 :     auto &current_map = stack.back ();
     217         8151 :     auto it = current_map.find (fragment.fragment_ident);
     218              : 
     219         8151 :     if (it == current_map.end ())
     220         8151 :       current_map.emplace (fragment.fragment_ident,
     221        16302 :                            MatchedFragmentContainer::metavar (fragment));
     222              :     else
     223            0 :       rust_unreachable ();
     224         8151 :   }
     225              : 
     226              :   /**
     227              :    * Append a new matched fragment to a repetition into the current substitution
     228              :    * map
     229              :    */
     230              :   void append_fragment (MatchedFragment fragment)
     231              :   {
     232              :     auto &current_map = stack.back ();
     233              :     auto it = current_map.find (fragment.fragment_ident);
     234              : 
     235              :     if (it == current_map.end ())
     236              :       it = current_map
     237              :              .emplace (fragment.fragment_ident,
     238              :                        std::unique_ptr<MatchedFragmentContainer> (
     239              :                          new MatchedFragmentContainerRepetition ()))
     240              :              .first;
     241              : 
     242              :     it->second->add_fragment (fragment);
     243              :   }
     244              : 
     245              :   /**
     246              :    * Append a new matched fragment to a repetition into the current substitution
     247              :    * map
     248              :    */
     249         4440 :   void append_fragment (std::string ident,
     250              :                         std::unique_ptr<MatchedFragmentContainer> fragment)
     251              :   {
     252         4440 :     auto &current_map = stack.back ();
     253         4440 :     auto it = current_map.find (ident);
     254              : 
     255         4440 :     if (it == current_map.end ())
     256         3424 :       it = current_map
     257         1712 :              .emplace (ident, std::unique_ptr<MatchedFragmentContainer> (
     258         1712 :                                 new MatchedFragmentContainerRepetition ()))
     259              :              .first;
     260              : 
     261         4440 :     it->second->add_fragment (std::move (fragment));
     262         4440 :   }
     263              : 
     264           57 :   void insert_matches (std::string key,
     265              :                        std::unique_ptr<MatchedFragmentContainer> matches)
     266              :   {
     267           57 :     auto &current_map = stack.back ();
     268           57 :     auto it = current_map.find (key);
     269           57 :     rust_assert (it == current_map.end ());
     270              : 
     271           57 :     current_map.emplace (std::move (key), std::move (matches));
     272           57 :   }
     273              : 
     274              : private:
     275              :   std::vector<std::map<std::string, std::unique_ptr<MatchedFragmentContainer>>>
     276              :     stack;
     277              : };
     278              : 
     279              : // Object used to store shared data (between functions) for macro expansion.
     280              : struct MacroExpander
     281              : {
     282              :   enum class ContextType
     283              :   {
     284              :     ITEM,
     285              :     STMT,
     286              :     EXPR,
     287              :     EXTERN,
     288              :     TYPE,
     289              :     TRAIT,
     290              :     IMPL,
     291              :     TRAIT_IMPL,
     292              :     PATTERN,
     293              :   };
     294              : 
     295              :   ExpansionCfg cfg;
     296              :   unsigned int expansion_depth = 0;
     297              : 
     298         4510 :   MacroExpander (AST::Crate &crate, ExpansionCfg cfg, Session &session)
     299         4510 :     : cfg (cfg), crate (crate), session (session),
     300         4510 :       sub_stack (SubstitutionScope ()),
     301         4510 :       expanded_fragment (AST::Fragment::create_error ()),
     302         4510 :       has_changed_flag (false), had_duplicate_error (false),
     303         4510 :       resolver (Resolver::Resolver::get ()),
     304         4510 :       mappings (Analysis::Mappings::get ())
     305         4510 :   {}
     306              : 
     307         9020 :   ~MacroExpander () = default;
     308              : 
     309              :   // Expands all macros in the crate passed in.
     310              :   void expand_crate ();
     311              : 
     312              :   /**
     313              :    * Expand the eager invocations contained within a builtin macro invocation.
     314              :    * Called by `expand_invoc` when expanding builtin invocations.
     315              :    */
     316              :   void expand_eager_invocations (AST::MacroInvocation &invoc);
     317              : 
     318              :   /* Expands a macro invocation - possibly make both
     319              :    * have similar duck-typed interface and use templates?*/
     320              :   // should this be public or private?
     321              :   void expand_invoc (AST::MacroInvocation &invoc, AST::InvocKind semicolon);
     322              : 
     323              :   // Expands a single declarative macro.
     324              :   AST::Fragment expand_decl_macro (location_t locus, AST::MacroInvocData &invoc,
     325              :                                    AST::MacroRulesDefinition &rules_def,
     326              :                                    AST::InvocKind semicolon);
     327              : 
     328              :   bool depth_exceeds_recursion_limit () const;
     329              : 
     330              :   bool try_match_rule (AST::MacroRule &match_rule,
     331              :                        AST::DelimTokenTree &invoc_token_tree);
     332              : 
     333              :   AST::Fragment transcribe_rule (
     334              :     AST::MacroRulesDefinition &definition, AST::MacroRule &match_rule,
     335              :     AST::DelimTokenTree &invoc_token_tree,
     336              :     std::map<std::string, MatchedFragmentContainer *> &matched_fragments,
     337              :     AST::InvocKind invoc_kind, ContextType ctx);
     338              : 
     339              :   bool match_fragment (Parser<MacroInvocLexer> &parser,
     340              :                        AST::MacroMatchFragment &fragment);
     341              : 
     342              :   bool match_token (Parser<MacroInvocLexer> &parser, AST::Token &token);
     343              : 
     344              :   void match_repetition_skipped_metavars (AST::MacroMatch &);
     345              :   void match_repetition_skipped_metavars (AST::MacroMatchFragment &);
     346              :   void match_repetition_skipped_metavars (AST::MacroMatchRepetition &);
     347              :   void match_repetition_skipped_metavars (AST::MacroMatcher &);
     348              : 
     349              :   bool match_repetition (Parser<MacroInvocLexer> &parser,
     350              :                          AST::MacroMatchRepetition &rep);
     351              : 
     352              :   bool match_matcher (Parser<MacroInvocLexer> &parser,
     353              :                       AST::MacroMatcher &matcher, bool in_repetition = false,
     354              :                       bool match_delim = true);
     355              : 
     356              :   /**
     357              :    * Match any amount of matches
     358              :    *
     359              :    * @param parser Parser to use for matching
     360              :    * @param rep Repetition to try and match
     361              :    * @param match_amount Reference in which to store the amount of successful
     362              :    * and valid matches
     363              :    *
     364              :    * @param lo_bound Lower bound of the matcher. When specified, the matcher
     365              :    * will only succeed if it parses at *least* `lo_bound` fragments. If
     366              :    * unspecified, the matcher could succeed when parsing 0 fragments.
     367              :    *
     368              :    * @param hi_bound Higher bound of the matcher. When specified, the matcher
     369              :    * will only succeed if it parses *less than* `hi_bound` fragments. If
     370              :    * unspecified, the matcher could succeed when parsing an infinity of
     371              :    * fragments.
     372              :    *
     373              :    * @return true if matching was successful and within the given limits, false
     374              :    * otherwise
     375              :    */
     376              :   bool match_n_matches (Parser<MacroInvocLexer> &parser,
     377              :                         AST::MacroMatchRepetition &rep, size_t &match_amount,
     378              :                         size_t lo_bound = 0, size_t hi_bound = 0);
     379              : 
     380      1610378 :   void push_context (ContextType t) { context.push_back (t); }
     381              : 
     382      1610378 :   ContextType pop_context ()
     383              :   {
     384      1610378 :     rust_assert (!context.empty ());
     385              : 
     386      1610378 :     ContextType t = context.back ();
     387      1610378 :     context.pop_back ();
     388              : 
     389      1610378 :     return t;
     390              :   }
     391              : 
     392         2436 :   ContextType peek_context () { return context.back (); }
     393              : 
     394         2818 :   void set_expanded_fragment (AST::Fragment &&fragment)
     395              :   {
     396         2818 :     if (!fragment.is_error ())
     397         2818 :       has_changed_flag = true;
     398              : 
     399         2818 :     expanded_fragment = std::move (fragment);
     400         2818 :   }
     401              : 
     402      1635515 :   AST::Fragment take_expanded_fragment ()
     403              :   {
     404      1635515 :     auto fragment = std::move (expanded_fragment);
     405      1635515 :     expanded_fragment = AST::Fragment::create_error ();
     406              : 
     407      1635515 :     return fragment;
     408              :   }
     409              : 
     410              :   void import_proc_macros (std::string extern_crate);
     411              : 
     412              :   template <typename T>
     413            0 :   AST::Fragment expand_derive_proc_macro (T &item, AST::SimplePath &path)
     414              :   {
     415              :     tl::optional<CustomDeriveProcMacro &> macro
     416            0 :       = mappings.lookup_derive_proc_macro_invocation (path);
     417            0 :     if (!macro.has_value ())
     418              :       {
     419            0 :         rust_error_at (path.get_locus (), "macro not found");
     420            0 :         return AST::Fragment::create_error ();
     421              :       }
     422              : 
     423            0 :     AST::TokenCollector collector;
     424              : 
     425            0 :     collector.visit (item);
     426              : 
     427            0 :     auto c = collector.collect_tokens ();
     428            0 :     std::vector<const_TokenPtr> vec (c.cbegin (), c.cend ());
     429              : 
     430              :     return parse_proc_macro_output (
     431            0 :       macro.value ().get_handle () (convert (vec)));
     432            0 :   }
     433              : 
     434              :   template <typename T>
     435              :   AST::Fragment expand_bang_proc_macro (T &item,
     436              :                                         AST::MacroInvocation &invocation)
     437              :   {
     438              :     tl::optional<BangProcMacro &> macro
     439              :       = mappings.lookup_bang_proc_macro_invocation (invocation);
     440              :     if (!macro.has_value ())
     441              :       {
     442              :         rust_error_at (invocation.get_locus (), "macro not found");
     443              :         return AST::Fragment::create_error ();
     444              :       }
     445              : 
     446              :     AST::TokenCollector collector;
     447              : 
     448              :     collector.visit (item);
     449              : 
     450              :     auto c = collector.collect_tokens ();
     451              :     std::vector<const_TokenPtr> vec (c.cbegin (), c.cend ());
     452              : 
     453              :     return parse_proc_macro_output (
     454              :       macro.value ().get_handle () (convert (vec)));
     455              :   }
     456              : 
     457              :   template <typename T>
     458            1 :   AST::Fragment expand_attribute_proc_macro (T &item, AST::SimplePath &path)
     459              :   {
     460              :     tl::optional<AttributeProcMacro &> macro
     461            1 :       = mappings.lookup_attribute_proc_macro_invocation (path);
     462            1 :     if (!macro.has_value ())
     463              :       {
     464            1 :         rust_error_at (path.get_locus (), "macro not found");
     465            1 :         return AST::Fragment::create_error ();
     466              :       }
     467              : 
     468            0 :     AST::TokenCollector collector;
     469              : 
     470            0 :     collector.visit (item);
     471              : 
     472            0 :     auto c = collector.collect_tokens ();
     473            0 :     std::vector<const_TokenPtr> vec (c.cbegin (), c.cend ());
     474              : 
     475              :     // FIXME: Handle attributes
     476              :     return parse_proc_macro_output (
     477            0 :       macro.value ().get_handle () (ProcMacro::TokenStream::make_tokenstream (),
     478            0 :                                     convert (vec)));
     479            0 :   }
     480              : 
     481              :   /**
     482              :    * Has the MacroExpander expanded a macro since its state was last reset?
     483              :    */
     484        10347 :   bool has_changed () const { return has_changed_flag; }
     485              : 
     486              :   /**
     487              :    * Reset the expander's "changed" state. This function should be executed at
     488              :    * each iteration in a fixed point loop
     489              :    */
     490        10347 :   void reset_changed_state () { has_changed_flag = false; }
     491              : 
     492            1 :   tl::optional<AST::MacroRulesDefinition &> &get_last_definition ()
     493              :   {
     494            1 :     return last_def;
     495              :   }
     496              : 
     497            1 :   tl::optional<AST::MacroInvocation &> &get_last_invocation ()
     498              :   {
     499            1 :     return last_invoc;
     500              :   }
     501              : 
     502              : private:
     503              :   AST::Fragment parse_proc_macro_output (ProcMacro::TokenStream ts);
     504              : 
     505              :   AST::Crate &crate;
     506              :   Session &session;
     507              :   SubstitutionScope sub_stack;
     508              :   std::vector<ContextType> context;
     509              :   AST::Fragment expanded_fragment;
     510              :   bool has_changed_flag;
     511              : 
     512              :   tl::optional<AST::MacroRulesDefinition &> last_def;
     513              :   tl::optional<AST::MacroInvocation &> last_invoc;
     514              : 
     515              :   // used to avoid emitting excess errors
     516              :   bool had_duplicate_error;
     517              : 
     518              : public:
     519              :   Resolver::Resolver *resolver;
     520              :   Analysis::Mappings &mappings;
     521              : };
     522              : 
     523              : } // namespace Rust
     524              : 
     525              : #endif
        

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.