LCOV - code coverage report
Current view: top level - gcc/rust/checks/lints - rust-lint-marklive.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 93.9 % 132 124
Test Date: 2026-08-08 15:37:49 Functions: 100.0 % 13 13
Legend: Lines:     hit not hit

            Line data    Source code
       1              : // Copyright (C) 2021-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              : // The idea is that all reachable symbols are live, codes called
      20              : // from live codes are live, and everything else is dead.
      21              : 
      22              : #include "rust-lint-marklive.h"
      23              : #include "options.h"
      24              : #include "rust-hir-full.h"
      25              : #include "rust-hir-map.h"
      26              : #include "rust-hir-path.h"
      27              : #include "rust-name-resolver.h"
      28              : #include "rust-finalized-name-resolution-context.h"
      29              : #include "rust-rib.h"
      30              : #include "rust-system.h"
      31              : #include "rust-tyty.h"
      32              : 
      33              : namespace Rust {
      34              : namespace Analysis {
      35              : 
      36              : // This class trys to find the live symbols which can be used as
      37              : // seeds in MarkLive
      38              : //
      39              : // 1. TODO: explicit live
      40              : //    - Attribute like #[allow(dead_code)]
      41              : //    - Attribute like #[lang=".."], it's not a intra-crate item.
      42              : // 2. TODO: foreign item
      43         8610 : class FindEntryPoint : public MarkLiveBase
      44              : {
      45              :   using Rust::Analysis::MarkLiveBase::visit;
      46              : 
      47              : public:
      48         4305 :   static std::vector<HirId> find (HIR::Crate &crate)
      49              :   {
      50         8610 :     FindEntryPoint findEntryPoint;
      51        22114 :     for (auto &it : crate.get_items ())
      52        17809 :       it->accept_vis (findEntryPoint);
      53         4305 :     return findEntryPoint.getEntryPoint ();
      54         4305 :   }
      55              : 
      56              :   // TODO not only fn main can be a entry point.
      57         5979 :   void visit (HIR::Function &function) override
      58              :   {
      59         5979 :     if (function.get_function_name ().as_string () == "main")
      60              :       {
      61         3935 :         entryPoints.push_back (function.get_mappings ().get_hirid ());
      62              :       }
      63         5979 :   }
      64              : 
      65              : private:
      66         4305 :   FindEntryPoint () : MarkLiveBase () {}
      67              :   std::vector<HirId> entryPoints;
      68         4305 :   std::vector<HirId> getEntryPoint () { return entryPoints; }
      69              : };
      70              : 
      71              : std::set<HirId>
      72         4305 : MarkLive::Analysis (HIR::Crate &crate)
      73              : {
      74         4305 :   MarkLive marklive (FindEntryPoint::find (crate));
      75         4305 :   marklive.go (crate);
      76              : 
      77         4305 :   return marklive.liveSymbols;
      78         4305 : }
      79              : 
      80              : // pop a live symbol from worklist every iteration,
      81              : // if it's a function then walk the function body, and
      82              : // 1. save all the live symbols in worklist which is
      83              : //    visited first time
      84              : // 2. save all the live symbols in liveSymbols
      85              : void
      86         4305 : MarkLive::go (HIR::Crate &)
      87              : {
      88        45892 :   while (!worklist.empty ())
      89              :     {
      90        41587 :       HirId hirId = worklist.back ();
      91        41587 :       worklist.pop_back ();
      92        41587 :       scannedSymbols.emplace (hirId);
      93        41587 :       liveSymbols.emplace (hirId);
      94        41587 :       if (auto item = mappings.lookup_hir_item (hirId))
      95        16742 :         item.value ()->accept_vis (*this);
      96        24845 :       else if (auto implItem = mappings.lookup_hir_implitem (hirId))
      97         1197 :         implItem->first->accept_vis (*this);
      98              :     }
      99         4305 : }
     100              : 
     101              : void
     102        46032 : MarkLive::visit (HIR::PathInExpression &expr)
     103              : {
     104              :   // We should iterate every path segment in order to mark the struct which
     105              :   // is used in expression like Foo::bar(), we should mark the Foo alive.
     106        46032 :   if (!expr.is_lang_item ())
     107        45983 :     expr.iterate_path_segments ([&] (HIR::PathExprSegment &seg) -> bool {
     108        50409 :       return visit_path_segment (seg);
     109              :     });
     110              : 
     111              :   // after iterate the path segments, we should mark functions and associated
     112              :   // functions alive.
     113        46032 :   NodeId ast_node_id = expr.get_mappings ().get_nodeid ();
     114        46032 :   NodeId ref_node_id = UNKNOWN_NODEID;
     115              : 
     116        46032 :   if (expr.is_lang_item ())
     117           49 :     ref_node_id
     118           49 :       = Analysis::Mappings::get ().get_lang_item_node (expr.get_lang_item ());
     119              :   else
     120        45983 :     find_value_definition (ast_node_id, ref_node_id);
     121              : 
     122              :   // node back to HIR
     123        46032 :   tl::optional<HirId> hid = mappings.lookup_node_to_hir (ref_node_id);
     124        46032 :   rust_assert (hid.has_value ());
     125        46032 :   auto ref = hid.value ();
     126              : 
     127              :   // it must resolve to some kind of HIR::Item or HIR::InheritImplItem
     128        46032 :   if (auto resolved_item = mappings.lookup_hir_item (ref))
     129         6659 :     mark_hir_id (resolved_item.value ()->get_mappings ().get_hirid ());
     130        39373 :   else if (auto resolved_item = mappings.lookup_hir_implitem (ref))
     131          487 :     mark_hir_id (resolved_item->first->get_impl_mappings ().get_hirid ());
     132        46032 : }
     133              : 
     134              : void
     135         1598 : MarkLive::visit (HIR::MethodCallExpr &expr)
     136              : {
     137         1598 :   expr.get_receiver ().accept_vis (*this);
     138         1598 :   visit_path_segment (expr.get_method_name ());
     139         2152 :   for (auto &argument : expr.get_arguments ())
     140          554 :     argument->accept_vis (*this);
     141              : 
     142              :   // Trying to find the method definition and mark it alive.
     143         1598 :   NodeId ast_node_id = expr.get_mappings ().get_nodeid ();
     144         1598 :   NodeId ref_node_id = UNKNOWN_NODEID;
     145         1598 :   find_value_definition (ast_node_id, ref_node_id);
     146              : 
     147              :   // node back to HIR
     148         1598 :   if (auto hid = mappings.lookup_node_to_hir (ref_node_id))
     149         1598 :     mark_hir_id (*hid);
     150              :   else
     151            0 :     rust_unreachable ();
     152         1598 : }
     153              : 
     154              : bool
     155        52007 : MarkLive::visit_path_segment (HIR::PathExprSegment seg)
     156              : {
     157        52007 :   if (seg.has_generic_args ())
     158              :     {
     159         1298 :       for (auto &type : seg.get_generic_args ().get_type_args ())
     160              :         {
     161          649 :           NodeId node_id = type->get_mappings ().get_nodeid ();
     162              : 
     163          649 :           if (auto resolved
     164          649 :               = resolver.lookup (node_id, Resolver2_0::Namespace::Types))
     165              :             {
     166          599 :               if (auto hid = mappings.lookup_node_to_hir (*resolved))
     167          599 :                 mark_hir_id (*hid);
     168              :             }
     169              :         }
     170              :     }
     171              : 
     172        52007 :   NodeId ast_node_id = seg.get_mappings ().get_nodeid ();
     173        52007 :   NodeId ref_node_id = UNKNOWN_NODEID;
     174              : 
     175              :   // There are two different kinds of segment for us.
     176              :   // 1. function segment
     177              :   //      like the symbol "foo" in expression `foo()`.
     178              :   // 2. type segment
     179              :   //      like the symbol "Foo" in expression `Foo{a: 1, b: 2}`
     180              :   //
     181              :   // We should mark them alive all and ignoring other kind of segments.
     182              :   // If the segment we dont care then just return false is fine
     183              :   // TODO: Should we look that up in all namespaces?
     184              : 
     185       104014 :   if (auto nslookup
     186        52007 :       = resolver.lookup (ast_node_id, Resolver2_0::Namespace::Values,
     187        52007 :                          Resolver2_0::Namespace::Types))
     188        49540 :     ref_node_id = nslookup->id;
     189              :   else
     190         2467 :     return false;
     191        49540 :   if (auto hid = mappings.lookup_node_to_hir (ref_node_id))
     192              :     {
     193        49540 :       mark_hir_id (*hid);
     194        49540 :       return true;
     195              :     }
     196            0 :   rust_unreachable ();
     197              : }
     198              : 
     199              : void
     200         3573 : MarkLive::visit (HIR::FieldAccessExpr &expr)
     201              : {
     202              :   // visit receiver at first
     203         3573 :   expr.get_receiver_expr ().accept_vis (*this);
     204              : 
     205              :   // resolve the receiver back to ADT type
     206         3573 :   TyTy::BaseType *receiver = nullptr;
     207         3573 :   if (!tyctx->lookup_type (
     208         3573 :         expr.get_receiver_expr ().get_mappings ().get_hirid (), &receiver))
     209              :     {
     210            0 :       rust_error_at (expr.get_receiver_expr ().get_locus (),
     211              :                      "unresolved type for receiver");
     212              :     }
     213              : 
     214         3573 :   TyTy::ADTType *adt = nullptr;
     215         3573 :   if (receiver->get_kind () == TyTy::TypeKind::ADT)
     216              :     {
     217         3093 :       adt = static_cast<TyTy::ADTType *> (receiver);
     218              : 
     219         3093 :       if (auto inner_ty = TyTy::try_get_box_inner_type (receiver))
     220              :         {
     221            1 :           rust_assert ((*inner_ty)->get_kind () == TyTy::TypeKind::ADT);
     222            1 :           adt = static_cast<TyTy::ADTType *> (*inner_ty);
     223              :         }
     224              :     }
     225          480 :   else if (receiver->get_kind () == TyTy::TypeKind::REF)
     226              :     {
     227          480 :       TyTy::ReferenceType *r = static_cast<TyTy::ReferenceType *> (receiver);
     228          480 :       TyTy::BaseType *b = r->get_base ();
     229          480 :       rust_assert (b->get_kind () == TyTy::TypeKind::ADT);
     230              : 
     231              :       adt = static_cast<TyTy::ADTType *> (b);
     232              :     }
     233              : 
     234         3093 :   rust_assert (adt != nullptr);
     235         3573 :   rust_assert (!adt->is_enum ());
     236         3573 :   rust_assert (adt->number_of_variants () == 1);
     237              : 
     238         3573 :   TyTy::VariantDef *variant = adt->get_variants ().at (0);
     239              : 
     240              :   // get the field index
     241         3573 :   size_t index;
     242         3573 :   TyTy::StructFieldType *field;
     243         3573 :   bool ok = variant->lookup_field (expr.get_field_name ().as_string (), &field,
     244              :                                    &index);
     245         3573 :   rust_assert (ok);
     246         3573 :   if (index >= variant->num_fields ())
     247              :     {
     248            0 :       rust_error_at (expr.get_receiver_expr ().get_locus (),
     249              :                      "cannot access struct %s by index: %lu",
     250            0 :                      adt->get_name ().c_str (), (unsigned long) index);
     251            0 :       return;
     252              :     }
     253              : 
     254              :   // get the field hir id
     255         3573 :   HirId field_id = field->get_ref ();
     256         3573 :   mark_hir_id (field_id);
     257              : }
     258              : 
     259              : void
     260          571 : MarkLive::visit (HIR::TupleIndexExpr &expr)
     261              : {
     262              :   // TODO: unused tuple field detection
     263          571 :   expr.get_tuple_expr ().accept_vis (*this);
     264          571 : }
     265              : 
     266              : void
     267           14 : MarkLive::visit (HIR::TypeAlias &alias)
     268              : {
     269           14 :   NodeId ast_node_id;
     270              : 
     271           14 :   if (auto id = resolver.lookup (
     272           14 :         alias.get_type_aliased ().get_mappings ().get_nodeid (),
     273           14 :         Resolver2_0::Namespace::Types))
     274           14 :     ast_node_id = *id;
     275              :   else
     276            0 :     rust_unreachable ();
     277              : 
     278           14 :   if (auto hid = mappings.lookup_node_to_hir (ast_node_id))
     279           14 :     mark_hir_id (*hid);
     280              :   else
     281            0 :     rust_unreachable ();
     282           14 : }
     283              : 
     284              : void
     285        62470 : MarkLive::mark_hir_id (HirId id)
     286              : {
     287        62470 :   if (scannedSymbols.find (id) == scannedSymbols.end ())
     288              :     {
     289        37652 :       worklist.push_back (id);
     290              :     }
     291        62470 :   liveSymbols.emplace (id);
     292        62470 : }
     293              : 
     294              : void
     295        47581 : MarkLive::find_value_definition (NodeId ast_node_id, NodeId &ref_node_id)
     296              : {
     297        47581 :   auto resolved = resolver.lookup (ast_node_id, Resolver2_0::Namespace::Values,
     298              :                                    Resolver2_0::Namespace::Types);
     299        47581 :   rust_assert (resolved.has_value ());
     300              : 
     301        47581 :   ref_node_id = resolved->id;
     302        47581 : }
     303              : 
     304              : } // namespace Analysis
     305              : } // 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.