LCOV - code coverage report
Current view: top level - gcc/rust/resolve - rust-early-name-resolver-2.0.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 86.7 % 263 228
Test Date: 2026-03-28 14:25:54 Functions: 92.0 % 25 23
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              : #include "rust-early-name-resolver-2.0.h"
      20              : #include "optional.h"
      21              : #include "options.h"
      22              : #include "rust-ast.h"
      23              : #include "rust-diagnostics.h"
      24              : #include "rust-hir-map.h"
      25              : #include "rust-item.h"
      26              : #include "rust-toplevel-name-resolver-2.0.h"
      27              : #include "rust-attributes.h"
      28              : #include "rust-finalize-imports-2.0.h"
      29              : #include "rust-attribute-values.h"
      30              : 
      31              : namespace Rust {
      32              : namespace Resolver2_0 {
      33              : 
      34        10431 : Early::Early (NameResolutionContext &ctx)
      35        10431 :   : DefaultResolver (ctx), toplevel (TopLevel (ctx)), dirty (false)
      36        10431 : {}
      37              : 
      38              : void
      39         2805 : Early::insert_once (AST::MacroInvocation &invocation, NodeId resolved)
      40              : {
      41              :   // TODO: Should we use `ctx.mark_resolved()`?
      42         2805 :   auto definition = ctx.mappings.lookup_macro_def (resolved);
      43              : 
      44         2805 :   if (!ctx.mappings.lookup_macro_invocation (invocation))
      45         2805 :     ctx.mappings.insert_macro_invocation (invocation, definition.value ());
      46         2805 : }
      47              : 
      48              : void
      49         3785 : Early::insert_once (AST::MacroRulesDefinition &def)
      50              : {
      51              :   // TODO: Should we use `ctx.mark_resolved()`?
      52         3785 :   if (!ctx.mappings.lookup_macro_def (def.get_node_id ()))
      53            1 :     ctx.mappings.insert_macro_def (&def);
      54         3785 : }
      55              : 
      56              : void
      57        10431 : Early::go (AST::Crate &crate)
      58              : {
      59              :   // First we go through TopLevel resolution to get all our declared items
      60        10431 :   toplevel.go (crate);
      61              : 
      62              :   // We start with resolving the list of imports that `TopLevel` has built for
      63              :   // us
      64              : 
      65        10431 :   dirty = toplevel.is_dirty ();
      66              :   // We now proceed with resolving macros, which can be nested in almost any
      67              :   // items
      68        10431 :   textual_scope.push ();
      69              : 
      70        10431 :   visit (crate);
      71              : 
      72        10431 :   textual_scope.pop ();
      73        10431 : }
      74              : 
      75              : bool
      76           19 : Early::resolve_glob_import (NodeId use_dec_id, TopLevel::ImportKind &&glob)
      77              : {
      78           19 :   auto resolved = ctx.resolve_path (glob.to_resolve, Namespace::Types);
      79           19 :   if (!resolved.has_value ())
      80              :     return false;
      81              : 
      82           18 :   auto result = Analysis::Mappings::get ().lookup_glob_container (
      83              :     resolved->get_node_id ());
      84              : 
      85           18 :   if (!result)
      86              :     return false;
      87              : 
      88              :   // here, we insert the module's NodeId into the import_mappings and will look
      89              :   // up the module proper in `FinalizeImports`
      90              :   // The namespace does not matter here since we are dealing with a glob
      91              :   // TODO: Ugly
      92           36 :   import_mappings.insert (use_dec_id,
      93           18 :                           ImportPair (std::move (glob),
      94           54 :                                       ImportData::Glob (*resolved)));
      95              : 
      96           18 :   return true;
      97           19 : }
      98              : 
      99              : bool
     100            0 : Early::resolve_simple_import (NodeId use_dec_id, TopLevel::ImportKind &&import)
     101              : {
     102            0 :   auto definitions = resolve_path_in_all_ns (import.to_resolve);
     103              : 
     104              :   // if we've found at least one definition, then we're good
     105            0 :   if (definitions.empty ())
     106              :     return false;
     107              : 
     108            0 :   auto &imports = import_mappings.new_or_access (use_dec_id);
     109              : 
     110            0 :   imports.emplace_back (
     111            0 :     ImportPair (std::move (import),
     112            0 :                 ImportData::Simple (std::move (definitions))));
     113              : 
     114            0 :   return true;
     115            0 : }
     116              : 
     117              : bool
     118         3053 : Early::resolve_rebind_import (NodeId use_dec_id,
     119              :                               TopLevel::ImportKind &&rebind_import)
     120              : {
     121         3053 :   auto definitions = resolve_path_in_all_ns (rebind_import.to_resolve);
     122              : 
     123              :   // if we've found at least one definition, then we're good
     124         3053 :   if (definitions.empty ())
     125              :     return false;
     126         6147 :   for (const auto &def : definitions)
     127              :     {
     128         3106 :       if (def.first.is_ambiguous ())
     129              :         {
     130            1 :           rich_location rich_locus (line_table,
     131            1 :                                     rebind_import.to_resolve.get_locus ());
     132            1 :           rust_error_at (rich_locus, ErrorCode::E0659, "%qs is ambiguous",
     133            1 :                          rebind_import.to_resolve.as_string ().c_str ());
     134            1 :           return true;
     135            1 :         }
     136              :     }
     137              : 
     138         3041 :   auto &imports = import_mappings.new_or_access (use_dec_id);
     139              : 
     140         3041 :   imports.emplace_back (
     141         3041 :     ImportPair (std::move (rebind_import),
     142         6082 :                 ImportData::Rebind (std::move (definitions))));
     143              : 
     144         3041 :   return true;
     145         3053 : }
     146              : 
     147              : void
     148         1845 : Early::build_import_mapping (
     149              :   std::pair<NodeId, std::vector<TopLevel::ImportKind>> &&use_import)
     150              : {
     151         1845 :   auto found = false;
     152         1845 :   auto use_dec_id = use_import.first;
     153              : 
     154         4917 :   for (auto &&import : use_import.second)
     155              :     {
     156              :       // We create a copy of the path in case of errors, since the `import` will
     157              :       // be moved into the newly created import mappings
     158         3072 :       auto path = import.to_resolve;
     159              : 
     160              :       // used to skip the "unresolved import" error
     161              :       // if we output other errors during resolution
     162         3072 :       size_t old_error_count = macro_resolve_errors.size ();
     163              : 
     164         3072 :       switch (import.kind)
     165              :         {
     166           19 :         case TopLevel::ImportKind::Kind::Glob:
     167           19 :           found = resolve_glob_import (use_dec_id, std::move (import));
     168           19 :           break;
     169            0 :         case TopLevel::ImportKind::Kind::Simple:
     170            0 :           found = resolve_simple_import (use_dec_id, std::move (import));
     171            0 :           break;
     172         3053 :         case TopLevel::ImportKind::Kind::Rebind:
     173         3053 :           found = resolve_rebind_import (use_dec_id, std::move (import));
     174         3053 :           break;
     175              :         }
     176              : 
     177         3072 :       if (!found && old_error_count == macro_resolve_errors.size ())
     178           20 :         collect_error (Error (path.get_final_segment ().get_locus (),
     179              :                               ErrorCode::E0433, "unresolved import %qs",
     180           20 :                               path.as_string ().c_str ()));
     181         3072 :     }
     182         1845 : }
     183              : 
     184              : void
     185        66471 : Early::TextualScope::push ()
     186              : {
     187              :   // push a new empty scope
     188        66471 :   scopes.emplace_back ();
     189        66471 : }
     190              : 
     191              : void
     192        66471 : Early::TextualScope::pop ()
     193              : {
     194        66471 :   rust_assert (!scopes.empty ());
     195              : 
     196        66471 :   scopes.pop_back ();
     197        66471 : }
     198              : 
     199              : void
     200         3785 : Early::TextualScope::insert (std::string name, NodeId id)
     201              : {
     202         3785 :   rust_assert (!scopes.empty ());
     203              : 
     204              :   // we can ignore the return value as we always want the latest defined macro
     205              :   // to shadow a previous one - so if two macros have the same name and get
     206              :   // inserted with the same key, it's not a bug
     207         7570 :   scopes.back ().insert ({name, id});
     208         3785 : }
     209              : 
     210              : tl::optional<NodeId>
     211         2835 : Early::TextualScope::get (const std::string &name)
     212              : {
     213         5575 :   for (auto iterator = scopes.rbegin (); iterator != scopes.rend (); iterator++)
     214              :     {
     215         5532 :       auto scope = *iterator;
     216         5532 :       auto found = scope.find (name);
     217         5532 :       if (found != scope.end ())
     218         2792 :         return found->second;
     219         5532 :     }
     220              : 
     221           43 :   return tl::nullopt;
     222              : }
     223              : 
     224              : void
     225         3785 : Early::visit (AST::MacroRulesDefinition &def)
     226              : {
     227         3785 :   DefaultResolver::visit (def);
     228              : 
     229         7570 :   textual_scope.insert (def.get_rule_name ().as_string (), def.get_node_id ());
     230         3785 :   insert_once (def);
     231         3785 : }
     232              : 
     233              : void
     234        52810 : Early::visit (AST::BlockExpr &block)
     235              : {
     236        52810 :   textual_scope.push ();
     237              : 
     238        52810 :   DefaultResolver::visit (block);
     239              : 
     240        52810 :   textual_scope.pop ();
     241        52810 : }
     242              : 
     243              : void
     244         3261 : Early::visit (AST::Module &module)
     245              : {
     246         3261 :   bool is_macro_use = false;
     247              : 
     248         3504 :   for (const auto &attr : module.get_outer_attrs ())
     249              :     {
     250          274 :       if (attr.get_path ().as_string () == Values::Attributes::MACRO_USE)
     251              :         {
     252              :           is_macro_use = true;
     253              :           break;
     254              :         }
     255              :     }
     256              : 
     257         3261 :   if (!is_macro_use)
     258         3230 :     textual_scope.push ();
     259              : 
     260         3261 :   DefaultResolver::visit (module);
     261              : 
     262         3261 :   if (!is_macro_use)
     263         3230 :     textual_scope.pop ();
     264         3261 : }
     265              : 
     266              : void
     267         2847 : Early::visit (AST::MacroInvocation &invoc)
     268              : {
     269         2847 :   auto &path = invoc.get_invoc_data ().get_path ();
     270              : 
     271              :   // We special case the `offset_of!()` macro if the flag is here, otherwise
     272              :   // we accept whatever `offset_of!()` definition we resolved to.
     273         2847 :   auto resolve_offset_of
     274         2847 :     = flag_assume_builtin_offset_of && (path.as_string () == "offset_of");
     275              : 
     276         2847 :   if (invoc.get_kind () == AST::MacroInvocation::InvocKind::Builtin)
     277           89 :     for (auto &pending_invoc : invoc.get_pending_eager_invocations ())
     278           46 :       pending_invoc->accept_vis (*this);
     279              : 
     280              :   // When a macro is invoked by an unqualified identifier (not part of a
     281              :   // multi-part path), it is first looked up in textual scoping. If this does
     282              :   // not yield any results, then it is looked up in path-based scoping. If the
     283              :   // macro's name is qualified with a path, then it is only looked up in
     284              :   // path-based scoping.
     285              : 
     286              :   // https://doc.rust-lang.org/reference/macros-by-example.html#path-based-scope
     287              : 
     288         2847 :   tl::optional<Rib::Definition> definition = tl::nullopt;
     289         2847 :   if (path.get_segments ().size () == 1)
     290         2835 :     definition
     291         2835 :       = textual_scope.get (path.get_final_segment ().as_string ())
     292         8419 :           .map ([] (NodeId id) { return Rib::Definition::NonShadowable (id); });
     293              : 
     294              :   // we won't have changed `definition` from `nullopt` if there are more
     295              :   // than one segments in our path
     296         2847 :   if (!definition.has_value ())
     297           68 :     definition = ctx.resolve_path (path, Namespace::Macros);
     298              : 
     299              :   // if the definition still does not have a value, then it's an error - unless
     300              :   // we should automatically resolve offset_of!() calls
     301         2847 :   if (!definition.has_value ())
     302              :     {
     303           42 :       if (!resolve_offset_of)
     304           24 :         collect_error (Error (invoc.get_locus (), ErrorCode::E0433,
     305              :                               "could not resolve macro invocation %qs",
     306           48 :                               path.as_string ().c_str ()));
     307           42 :       return;
     308              :     }
     309              : 
     310         2805 :   insert_once (invoc, definition->get_node_id ());
     311              : 
     312              :   // now do we need to keep mappings or something? or insert "uses" into our
     313              :   // ForeverStack? can we do that? are mappings simpler?
     314         2805 :   auto &mappings = Analysis::Mappings::get ();
     315         2805 :   auto rules_def = mappings.lookup_macro_def (definition->get_node_id ());
     316              : 
     317              :   // Macro definition not found, maybe it is not expanded yet.
     318         2805 :   if (!rules_def)
     319              :     return;
     320              : 
     321         2805 :   if (mappings.lookup_macro_invocation (invoc))
     322              :     return;
     323              : 
     324            0 :   mappings.insert_macro_invocation (invoc, rules_def.value ());
     325         2847 : }
     326              : 
     327              : void
     328          166 : Early::visit_derive_attribute (AST::Attribute &attr,
     329              :                                Analysis::Mappings &mappings)
     330              : {
     331          166 :   auto traits = attr.get_traits_to_derive ();
     332          466 :   for (auto &trait : traits)
     333              :     {
     334          300 :       auto definition = ctx.resolve_path (trait.get (), Namespace::Macros);
     335          300 :       if (!definition.has_value ())
     336              :         {
     337              :           // FIXME: Change to proper error message
     338          600 :           collect_error (Error (trait.get ().get_locus (),
     339              :                                 "could not resolve trait %qs",
     340          300 :                                 trait.get ().as_string ().c_str ()));
     341          300 :           continue;
     342              :         }
     343              : 
     344            0 :       auto pm_def
     345            0 :         = mappings.lookup_derive_proc_macro_def (definition->get_node_id ());
     346              : 
     347            0 :       if (pm_def.has_value ())
     348            0 :         mappings.insert_derive_proc_macro_invocation (trait, pm_def.value ());
     349          300 :     }
     350          166 : }
     351              : 
     352              : void
     353            1 : Early::visit_non_builtin_attribute (AST::Attribute &attr,
     354              :                                     Analysis::Mappings &mappings,
     355              :                                     std::string &name)
     356              : {
     357            1 :   auto definition = ctx.resolve_path (attr.get_path (), Namespace::Macros);
     358            1 :   if (!definition.has_value ())
     359              :     {
     360              :       // FIXME: Change to proper error message
     361            0 :       collect_error (Error (attr.get_locus (),
     362              :                             "could not resolve attribute macro invocation %qs",
     363            0 :                             name.c_str ()));
     364            0 :       return;
     365              :     }
     366            1 :   auto pm_def
     367            1 :     = mappings.lookup_attribute_proc_macro_def (definition->get_node_id ());
     368              : 
     369            1 :   if (!pm_def.has_value ())
     370              :     return;
     371              : 
     372            0 :   mappings.insert_attribute_proc_macro_invocation (attr.get_path (),
     373            0 :                                                    pm_def.value ());
     374            1 : }
     375              : 
     376              : void
     377        69406 : Early::visit (AST::Attribute &attr)
     378              : {
     379        69406 :   auto &mappings = Analysis::Mappings::get ();
     380              : 
     381        69406 :   auto name = attr.get_path ().get_segments ().at (0).get_segment_name ();
     382       138646 :   auto is_not_builtin = [&name] (AST::Attribute &attr) {
     383        69240 :     return Analysis::BuiltinAttributeMappings::get ()
     384        69240 :       ->lookup_builtin (name)
     385        69240 :       .is_error ();
     386        69406 :   };
     387              : 
     388        69406 :   if (attr.is_derive ())
     389              :     {
     390          166 :       visit_derive_attribute (attr, mappings);
     391              :     }
     392        69240 :   else if (is_not_builtin (attr)) // Do not resolve builtins
     393              :     {
     394            1 :       visit_non_builtin_attribute (attr, mappings, name);
     395              :     }
     396        69406 : }
     397              : 
     398              : void
     399            0 : Early::finalize_simple_import (const Early::ImportPair &mapping)
     400              : {
     401              :   // FIXME: We probably need to store namespace information
     402              : 
     403            0 :   auto locus = mapping.import_kind.to_resolve.get_locus ();
     404            0 :   auto data = mapping.data;
     405            0 :   auto identifier
     406            0 :     = mapping.import_kind.to_resolve.get_final_segment ().get_segment_name ();
     407              : 
     408            0 :   for (auto &&definition : data.definitions ())
     409            0 :     toplevel
     410            0 :       .insert_or_error_out (
     411            0 :         identifier, locus, definition.first.get_node_id (), definition.second /* TODO: This isn't clear - it would be better if it was called .ns or something */);
     412            0 : }
     413              : 
     414              : void
     415           18 : Early::finalize_glob_import (NameResolutionContext &ctx,
     416              :                              const Early::ImportPair &mapping)
     417              : {
     418           36 :   auto container = Analysis::Mappings::get ().lookup_glob_container (
     419           36 :     mapping.data.container ().get_node_id ());
     420              : 
     421           18 :   rust_assert (container);
     422              : 
     423           18 :   if (mapping.import_kind.is_prelude)
     424              :     {
     425            1 :       rust_assert (container.value ()->get_glob_container_kind ()
     426              :                    == AST::GlobContainer::Kind::Module);
     427              : 
     428            1 :       ctx.prelude = mapping.data.container ().get_node_id ();
     429              :     }
     430              : 
     431           18 :   GlobbingVisitor (ctx).go (container.value ());
     432           18 : }
     433              : 
     434              : void
     435         3041 : Early::finalize_rebind_import (const Early::ImportPair &mapping)
     436              : {
     437              :   // We can fetch the value here as `resolve_rebind` will only be called on
     438              :   // imports of the right kind
     439         3041 :   auto &path = mapping.import_kind.to_resolve;
     440         3041 :   auto &rebind = mapping.import_kind.rebind.value ();
     441         3041 :   auto data = mapping.data;
     442              : 
     443         3041 :   location_t locus = UNKNOWN_LOCATION;
     444         3041 :   std::string declared_name;
     445              : 
     446              :   // FIXME: This needs to be done in `FinalizeImports`
     447         3041 :   switch (rebind.get_new_bind_type ())
     448              :     {
     449            6 :     case AST::UseTreeRebind::NewBindType::IDENTIFIER:
     450            6 :       declared_name = rebind.get_identifier ().as_string ();
     451            6 :       locus = rebind.get_identifier ().get_locus ();
     452            6 :       break;
     453         3031 :     case AST::UseTreeRebind::NewBindType::NONE:
     454         3031 :       {
     455         3031 :         const auto &segments = path.get_segments ();
     456              :         // We don't want to insert `self` with `use module::self`
     457         3031 :         if (path.get_final_segment ().is_lower_self_seg ())
     458              :           {
     459              :             // Erroneous `self` or `{self}` use declaration
     460          209 :             if (segments.size () == 1)
     461              :               break;
     462          204 :             declared_name = segments[segments.size () - 2].as_string ();
     463              :           }
     464              :         else
     465         2822 :           declared_name = path.get_final_segment ().as_string ();
     466         3026 :         locus = path.get_final_segment ().get_locus ();
     467         3026 :         break;
     468              :       }
     469            4 :     case AST::UseTreeRebind::NewBindType::WILDCARD:
     470              :       // We don't want to insert it into the trie
     471            4 :       return;
     472              :     }
     473              : 
     474         6138 :   for (auto &&definition : data.definitions ())
     475         3101 :     toplevel.insert_or_error_out (
     476        15441 :       declared_name, locus, definition.first.get_node_id (), definition.second /* TODO: This isn't clear - it would be better if it was called .ns or something */);
     477         6082 : }
     478              : 
     479              : void
     480         1845 : Early::visit (AST::UseDeclaration &decl)
     481              : {
     482              :   // We do not want to visit the use trees, we're only looking for top level
     483              :   // rebind. eg. `use something;` or `use something::other;`
     484         1845 :   if (decl.get_tree ()->get_kind () == AST::UseTree::Kind::Rebind)
     485              :     {
     486         1371 :       auto &rebind = static_cast<AST::UseTreeRebind &> (*decl.get_tree ());
     487         1371 :       if (rebind.get_path ().get_final_segment ().is_lower_self_seg ())
     488              :         {
     489            2 :           collect_error (
     490            1 :             Error (decl.get_locus (), ErrorCode::E0429,
     491            1 :                    "%<self%> imports are only allowed within a { } list"));
     492              :         }
     493              :     }
     494              : 
     495         1845 :   auto &imports = toplevel.get_imports_to_resolve ();
     496         1845 :   auto current_import = imports.find (decl.get_node_id ());
     497         1845 :   if (current_import != imports.end ())
     498              :     {
     499         1845 :       build_import_mapping (*current_import);
     500              :     }
     501              : 
     502              :   // Once this is done, we finalize their resolution
     503         4904 :   for (const auto &mapping : import_mappings.get (decl.get_node_id ()))
     504         3059 :     switch (mapping.import_kind.kind)
     505              :       {
     506           18 :       case TopLevel::ImportKind::Kind::Glob:
     507           18 :         finalize_glob_import (ctx, mapping);
     508           18 :         break;
     509            0 :       case TopLevel::ImportKind::Kind::Simple:
     510            0 :         finalize_simple_import (mapping);
     511            0 :         break;
     512         3041 :       case TopLevel::ImportKind::Kind::Rebind:
     513         3041 :         finalize_rebind_import (mapping);
     514         3041 :         break;
     515              :       }
     516              : 
     517         1845 :   DefaultResolver::visit (decl);
     518         1845 : }
     519              : 
     520              : void
     521          455 : Early::visit (AST::UseTreeList &use_list)
     522              : {
     523          455 :   if (!use_list.has_path ())
     524              :     {
     525           10 :       for (auto &&tree : use_list.get_trees ())
     526              :         {
     527            6 :           if (tree->get_kind () == AST::UseTree::Kind::Rebind)
     528              :             {
     529            6 :               auto &rebind = static_cast<AST::UseTreeRebind &> (*tree);
     530            6 :               auto path_size = rebind.get_path ().get_segments ().size ();
     531            6 :               if (path_size == 1
     532            6 :                   && rebind.get_path ()
     533            6 :                        .get_final_segment ()
     534            6 :                        .is_lower_self_seg ())
     535              :                 {
     536            4 :                   collect_error (Error (rebind.get_locus (), ErrorCode::E0431,
     537              :                                         "%<self%> import can only appear in an "
     538            4 :                                         "import list with a non-empty prefix"));
     539              :                 }
     540              :             }
     541              :         }
     542              :     }
     543          455 :   DefaultResolver::visit (use_list);
     544          455 : }
     545              : 
     546              : } // namespace Resolver2_0
     547              : } // namespace Rust
        

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.