LCOV - code coverage report
Current view: top level - gcc/rust/resolve - rust-name-resolution-context.hxx (source / functions) Coverage Total Hit
Test: gcc.info Lines: 87.4 % 151 132
Test Date: 2026-07-11 15:47:05 Functions: 57.1 % 14 8
Legend: Lines:     hit not hit

            Line data    Source code
       1              : // Copyright (C) 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 "optional.h"
      20              : #include "rust-forever-stack.h"
      21              : #include "rust-name-resolution-context.h"
      22              : 
      23              : /**
      24              :  * Split the actual path resolution logic in its own file because it's a lot,
      25              :  * and it could get even worse for certain edge cases.
      26              :  */
      27              : 
      28              : namespace Rust {
      29              : namespace Resolver2_0 {
      30              : 
      31              : template <Namespace N>
      32              : bool
      33        26167 : NameResolutionContext::should_search_prelude (
      34              :   const typename ForeverStack<N>::Node *current_node,
      35              :   const typename ForeverStack<N>::SegIterator &iterator,
      36              :   const std::vector<ResolutionPath::Segment> &segments)
      37              : {
      38              :   // Check whether the current_node is a root node
      39        26167 :   if (current_node->is_root ())
      40              :     return true;
      41              : 
      42              :   // Check whether we're at the start of a module (we can't travel elsewhere
      43              :   // from the start of a module)
      44        26150 :   if (is_start (iterator, segments)
      45        26150 :       && current_node->rib.kind == Rib::Kind::Module)
      46              :     return true;
      47              : 
      48              :   return false;
      49              : }
      50              : 
      51              : template <Namespace N>
      52              : tl::optional<Rib::Definition>
      53        87063 : NameResolutionContext::resolve_path (
      54              :   ForeverStack<N> &stack, const ResolutionPath &path, ResolutionMode mode,
      55              :   std::function<void (Usage, Definition, Namespace)> insert_segment_resolution,
      56              :   std::vector<Error> &collect_errors)
      57              : {
      58        87063 :   std::reference_wrapper<typename ForeverStack<N>::Node> starting_point
      59              :     = stack.cursor ();
      60              : 
      61        87063 :   return NameResolutionContext::resolve_path (stack, path, mode,
      62              :                                               insert_segment_resolution,
      63        87063 :                                               collect_errors, starting_point);
      64              : }
      65              : 
      66              : template <Namespace N>
      67              : tl::optional<Rib::Definition>
      68            0 : NameResolutionContext::resolve_path (
      69              :   ForeverStack<N> &stack, const ResolutionPath &path, ResolutionMode mode,
      70              :   std::function<void (Usage, Definition, Namespace)> insert_segment_resolution,
      71              :   std::vector<Error> &collect_errors, NodeId starting_point_id)
      72              : 
      73              : {
      74            0 :   auto starting_point = stack.dfs_node (stack.root, starting_point_id);
      75              : 
      76              :   // We may have a prelude, but haven't visited it yet and thus it's not in
      77              :   // our nodes
      78            0 :   if (!starting_point)
      79            0 :     return tl::nullopt;
      80              : 
      81            0 :   return NameResolutionContext::resolve_path (stack, path, mode,
      82              :                                               insert_segment_resolution,
      83            0 :                                               collect_errors, *starting_point);
      84              : }
      85              : 
      86              : template <Namespace N>
      87              : tl::optional<Rib::Definition>
      88        87063 : NameResolutionContext::resolve_path (
      89              :   ForeverStack<N> &stack, const ResolutionPath &path, ResolutionMode mode,
      90              :   std::function<void (Usage, Definition, Namespace)> insert_segment_resolution,
      91              :   std::vector<Error> &collect_errors,
      92              :   std::reference_wrapper<typename ForeverStack<N>::Node> starting_point)
      93              : {
      94        87063 :   bool can_descend = true;
      95              : 
      96        87063 :   rust_debug ("resolving %s", path.as_string ().c_str ());
      97              : 
      98        87063 :   if (auto lang_item = path.get_lang_prefix ())
      99              :     {
     100              :       NodeId seg_id
     101          392 :         = Analysis::Mappings::get ().get_lang_item_node (lang_item->first);
     102              : 
     103          392 :       insert_segment_resolution (Usage (lang_item->second), Definition (seg_id),
     104              :                                  N);
     105              : 
     106          392 :       if (path.get_segments ().empty ())
     107          392 :         return Rib::Definition::NonShadowable (seg_id);
     108              : 
     109            0 :       auto new_start = stack.dfs_node (stack.root, seg_id);
     110            0 :       rust_assert (new_start.has_value ());
     111            0 :       starting_point = new_start.value ();
     112              : 
     113            0 :       can_descend = false;
     114              :     }
     115              :   else
     116              :     {
     117        86671 :       switch (mode)
     118              :         {
     119              :         case ResolutionMode::Normal:
     120              :           break; // default
     121          952 :         case ResolutionMode::FromRoot:
     122          952 :           starting_point = stack.root;
     123          952 :           break;
     124            0 :         case ResolutionMode::FromExtern:
     125            0 :           starting_point = stack.extern_prelude;
     126            0 :           break;
     127            0 :         default:
     128            0 :           rust_unreachable ();
     129              :         }
     130              :     }
     131              : 
     132        86671 :   if (path.get_segments ().empty ())
     133            2 :     return Rib::Definition::NonShadowable (starting_point.get ().id);
     134              : 
     135        86669 :   auto &segments = path.get_segments ();
     136              : 
     137              :   // if there's only one segment, we just use `get`
     138       173338 :   if (can_descend && segments.size () == 1)
     139              :     {
     140        72528 :       auto &seg = segments.front ();
     141              : 
     142        72528 :       tl::optional<Rib::Definition> res
     143       290112 :         = stack.get (starting_point.get (), seg.name);
     144              : 
     145        72528 :       if (!res)
     146        52700 :         res = stack.get_lang_prelude (seg.name);
     147              : 
     148        55311 :       if (N == Namespace::Types && !res)
     149              :         {
     150          193 :           if (seg.is_crate_path_seg ())
     151              :             {
     152           53 :               insert_segment_resolution (Usage (seg.node_id),
     153           53 :                                          Definition (stack.root.id), N);
     154              :               // TODO: does NonShadowable matter?
     155           53 :               return Rib::Definition::NonShadowable (stack.root.id);
     156              :             }
     157          140 :           else if (seg.is_lower_self_seg ())
     158              :             {
     159            5 :               NodeId id = stack.find_closest_module (starting_point.get ()).id;
     160            5 :               insert_segment_resolution (Usage (seg.node_id), Definition (id),
     161              :                                          N);
     162              :               // TODO: does NonShadowable matter?
     163            5 :               return Rib::Definition::NonShadowable (id);
     164              :             }
     165          135 :           else if (seg.is_super_path_seg ())
     166              :             {
     167              :               auto &closest_module
     168            1 :                 = stack.find_closest_module (starting_point.get ());
     169            1 :               if (closest_module.is_root ())
     170              :                 {
     171            0 :                   rust_error_at (seg.locus, ErrorCode::E0433,
     172              :                                  "too many leading %<super%> keywords");
     173            0 :                   return tl::nullopt;
     174              :                 }
     175              : 
     176            1 :               NodeId id
     177            1 :                 = stack.find_closest_module (closest_module.parent.value ()).id;
     178            1 :               insert_segment_resolution (Usage (seg.node_id), Definition (id),
     179              :                                          N);
     180              :               // TODO: does NonShadowable matter?
     181            1 :               return Rib::Definition::NonShadowable (id);
     182              :             }
     183              :         }
     184              : 
     185              :       // FIXME: Is the NS to insert_segment_resolution valid?
     186        72469 :       if (res && !res->is_ambiguous ())
     187        71869 :         insert_segment_resolution (Usage (seg.node_id),
     188        71869 :                                    Definition (res->get_node_id ()), N);
     189        72469 :       return res;
     190        72528 :     }
     191              : 
     192        14141 :   auto iterator = segments.begin ();
     193        14141 :   if (can_descend)
     194              :     {
     195        14141 :       if (auto res = stack.find_starting_point (segments, starting_point,
     196              :                                                 insert_segment_resolution,
     197              :                                                 collect_errors))
     198        14139 :         iterator = *res;
     199              :       else
     200            2 :         return tl::nullopt;
     201              :     }
     202              : 
     203              :   // We do the first part of path resolution exclusively in the types NS - this
     204              :   // gives us a node in which to resolve the last segment of the path.
     205              : 
     206              :   // We take our starting point and then get the equivalent Node from the Types
     207              :   // NS - since it existed in whatever namespace we are right now, we assume it
     208              :   // exists in the types NS.
     209              :   auto types_starting_point
     210        14139 :     = types.dfs_node (types.root, starting_point.get ().id);
     211              : 
     212              :   // TODO: Add a method for converting a node between namespaces? Make all of
     213              :   // the starting point stuff return a NodeId rather than a Node&? And take a
     214              :   // NodeId as a starting point rather than a Node?
     215              : 
     216              :   auto node
     217        14139 :     = resolve_segments (types, types_starting_point.value (), segments,
     218              :                         iterator, insert_segment_resolution, collect_errors);
     219              : 
     220        14139 :   if (!node)
     221         1873 :     return tl::nullopt;
     222              : 
     223              :   // This node now represents the Node which *should* contain the definition
     224              :   // used by the last segment. We now get the equivalent Node from this
     225              :   // namespace after finding it in the types NS.
     226        12266 :   auto final_node_id = node.value ().id;
     227        12266 :   auto final_node = stack.dfs_node (stack.root, final_node_id).value ();
     228              : 
     229              :   // leave resolution within impl blocks to type checker
     230        12266 :   if (final_node.rib.kind == Rib::Kind::TraitOrImpl)
     231            0 :     return tl::nullopt;
     232              : 
     233        12266 :   auto &seg = segments.back ();
     234        15071 :   std::string seg_name = seg.name;
     235              : 
     236        12266 :   tl::optional<Rib::Definition> res
     237              :     = resolve_final_segment (stack, final_node, seg_name,
     238        12266 :                              seg.is_lower_self_seg ());
     239              :   // Ok we didn't find it in the rib, Lets try the prelude...
     240        12266 :   if (!res)
     241         4237 :     res = stack.get_lang_prelude (seg_name);
     242              : 
     243        12266 :   if (res && !res->is_ambiguous ())
     244         8027 :     insert_segment_resolution (Usage (seg.node_id),
     245         8027 :                                Definition (res->get_node_id ()), N);
     246              : 
     247        12266 :   return res;
     248        12266 : }
     249              : 
     250              : template <Namespace N>
     251              : tl::optional<typename ForeverStack<N>::Node &>
     252        14139 : NameResolutionContext::resolve_segments (
     253              :   ForeverStack<N> &stack, typename ForeverStack<N>::Node &starting_point,
     254              :   const std::vector<ResolutionPath::Segment> &segments,
     255              :   typename ForeverStack<N>::SegIterator iterator,
     256              :   std::function<void (Usage, Definition, Namespace)> insert_segment_resolution,
     257              :   std::vector<Error> &collect_errors)
     258              : {
     259        14139 :   auto *current_node = &starting_point;
     260        31653 :   for (; !is_last (iterator, segments); iterator++)
     261              :     {
     262        17514 :       auto &seg = *iterator;
     263              : 
     264        35028 :       std::string str = seg.name;
     265              : 
     266              :       // check that we don't encounter *any* leading keywords afterwards
     267        17514 :       if (check_leading_kw_at_start (collect_errors, seg,
     268        17514 :                                      seg.is_crate_path_seg ()
     269        17514 :                                        || seg.is_super_path_seg ()
     270        35021 :                                        || seg.is_lower_self_seg ()))
     271           10 :         return tl::nullopt;
     272              : 
     273        17504 :       tl::optional<std::reference_wrapper<typename ForeverStack<N>::Node>> child
     274              :         = tl::nullopt;
     275              : 
     276              :       /*
     277              :        * On every iteration this loop either
     278              :        *
     279              :        * 1. terminates
     280              :        *
     281              :        * 2. decreases the depth of the node pointed to by current_node until
     282              :        *    current_node reaches the root
     283              :        *
     284              :        * 3. If the root node is reached, and we were not able to resolve the
     285              :        *    segment, we search the prelude rib for the segment, by setting
     286              :        *    current_node to point to the prelude, and toggling the
     287              :        *    searched_prelude boolean to true. If current_node is the prelude
     288              :        *    rib, and searched_prelude is true, we will exit.
     289              :        *
     290              :        * This ensures termination.
     291              :        *
     292              :        */
     293        17504 :       bool searched_prelude = false;
     294        30313 :       while (true)
     295              :         {
     296        55916 :           if (is_start (iterator, segments)
     297        49791 :               && current_node->rib.kind == Rib::Kind::TraitOrImpl)
     298              :             {
     299              :               // we can't reference associated types/functions like this
     300         6125 :               current_node = &current_node->parent.value ();
     301         6144 :               continue;
     302              :             }
     303              : 
     304              :           // may set the value of child
     305       157371 :           for (auto &kv : current_node->children)
     306              :             {
     307       127058 :               auto &link = kv.first;
     308              : 
     309       254116 :               if (link.path.map_or (
     310        60822 :                     [&str] (Identifier path) {
     311        60822 :                       auto &path_str = path.as_string ();
     312        60822 :                       return str == path_str;
     313              :                     },
     314       127058 :                     false))
     315              :                 {
     316        57019 :                   child = kv.second;
     317              :                   break;
     318              :                 }
     319              :             }
     320              : 
     321        43666 :           if (child.has_value ())
     322              :             {
     323              :               break;
     324              :             }
     325              : 
     326        30313 :           auto rib_lookup = current_node->rib.get (seg.name);
     327        30313 :           if (rib_lookup && !rib_lookup->is_ambiguous ())
     328              :             {
     329         4135 :               if (Analysis::Mappings::get ()
     330         4135 :                     .lookup_glob_container (rib_lookup->get_node_id ())
     331         4135 :                     .has_value ())
     332              :                 {
     333         2288 :                   NodeId leaf_module
     334         2288 :                     = stack.find_leaf_definition (rib_lookup->get_node_id ())
     335         2288 :                         .value_or (Definition (rib_lookup->get_node_id ()))
     336              :                         .id;
     337              : 
     338         2288 :                   if (auto new_child = stack.dfs_node (stack.root, leaf_module))
     339              :                     {
     340         2288 :                       child = new_child.value ();
     341         2288 :                       break;
     342              :                     }
     343              :                   else
     344              :                     {
     345            0 :                       return tl::nullopt;
     346              :                     }
     347              :                 }
     348              :               else
     349              :                 {
     350              :                   // FIXME: Resolve segments always resolves in the Types NS
     351              :                   // correct? If so, we should remove the template parameter for
     352              :                   // this function - and use NS::Types directly here instead of
     353              :                   // N
     354         1847 :                   insert_segment_resolution (Usage (seg.node_id),
     355         1847 :                                              Definition (
     356              :                                                rib_lookup->get_node_id ()),
     357              :                                              N);
     358              : 
     359         1847 :                   return tl::nullopt;
     360              :                 }
     361              :             }
     362              : 
     363           19 :           if (!searched_prelude
     364        52326 :               && should_search_prelude<N> (current_node, iterator, segments))
     365              :             {
     366           19 :               searched_prelude = true;
     367           19 :               current_node = &stack.lang_prelude;
     368              :               continue;
     369              :             }
     370              : 
     371        26159 :           if (!is_start (iterator, segments)
     372        26154 :               || current_node->rib.kind == Rib::Kind::Module
     373        52313 :               || current_node->is_prelude ())
     374              :             {
     375           16 :               return tl::nullopt;
     376              :             }
     377              : 
     378        26143 :           current_node = &current_node->parent.value ();
     379              :         }
     380              : 
     381              :       // if child didn't point to a value
     382              :       // the while loop above would have returned or kept looping
     383        15641 :       current_node = &child->get ();
     384        15641 :       insert_segment_resolution (Usage (seg.node_id),
     385        15641 :                                  Definition (current_node->id), N);
     386              :     }
     387              : 
     388        12266 :   return *current_node;
     389              : }
     390              : 
     391              : template <>
     392              : inline tl::optional<Rib::Definition>
     393         2805 : NameResolutionContext::resolve_final_segment (
     394              :   ForeverStack<Namespace::Types> &stack,
     395              :   typename ForeverStack<Namespace::Types>::Node &final_node,
     396              :   std::string &seg_name, bool is_lower_self)
     397              : {
     398         2805 :   if (is_lower_self)
     399           54 :     return Rib::Definition::NonShadowable (final_node.id);
     400              :   else
     401         2751 :     return final_node.rib.get (seg_name);
     402              : }
     403              : 
     404              : template <Namespace N>
     405              : tl::optional<Rib::Definition>
     406         9461 : NameResolutionContext::resolve_final_segment (
     407              :   ForeverStack<N> &stack, typename ForeverStack<N>::Node &final_node,
     408              :   std::string &seg_name, bool is_lower_self)
     409              : {
     410         9461 :   return final_node.rib.get (seg_name);
     411              : }
     412              : 
     413              : } // namespace Resolver2_0
     414              : } // 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.