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.6 % 125 117
Test Date: 2026-07-11 15:47:05 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         8488 : class FindEntryPoint : public MarkLiveBase
      44              : {
      45              :   using Rust::Analysis::MarkLiveBase::visit;
      46              : 
      47              : public:
      48         4244 :   static std::vector<HirId> find (HIR::Crate &crate)
      49              :   {
      50         8488 :     FindEntryPoint findEntryPoint;
      51        21761 :     for (auto &it : crate.get_items ())
      52        17517 :       it->accept_vis (findEntryPoint);
      53         4244 :     return findEntryPoint.getEntryPoint ();
      54         4244 :   }
      55              : 
      56              :   // TODO not only fn main can be a entry point.
      57         5879 :   void visit (HIR::Function &function) override
      58              :   {
      59         5879 :     if (function.get_function_name ().as_string () == "main")
      60              :       {
      61         3890 :         entryPoints.push_back (function.get_mappings ().get_hirid ());
      62              :       }
      63         5879 :   }
      64              : 
      65              : private:
      66         4244 :   FindEntryPoint () : MarkLiveBase () {}
      67              :   std::vector<HirId> entryPoints;
      68         4244 :   std::vector<HirId> getEntryPoint () { return entryPoints; }
      69              : };
      70              : 
      71              : std::set<HirId>
      72         4244 : MarkLive::Analysis (HIR::Crate &crate)
      73              : {
      74         4244 :   MarkLive marklive (FindEntryPoint::find (crate));
      75         4244 :   marklive.go (crate);
      76              : 
      77         4244 :   return marklive.liveSymbols;
      78         4244 : }
      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         4244 : MarkLive::go (HIR::Crate &)
      87              : {
      88        44847 :   while (!worklist.empty ())
      89              :     {
      90        40603 :       HirId hirId = worklist.back ();
      91        40603 :       worklist.pop_back ();
      92        40603 :       scannedSymbols.emplace (hirId);
      93        40603 :       liveSymbols.emplace (hirId);
      94        40603 :       if (auto item = mappings.lookup_hir_item (hirId))
      95        16473 :         item.value ()->accept_vis (*this);
      96        24130 :       else if (auto implItem = mappings.lookup_hir_implitem (hirId))
      97         1196 :         implItem->first->accept_vis (*this);
      98              :     }
      99         4244 : }
     100              : 
     101              : void
     102        45346 : 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        45346 :   if (!expr.is_lang_item ())
     107        45297 :     expr.iterate_path_segments ([&] (HIR::PathExprSegment &seg) -> bool {
     108        49575 :       return visit_path_segment (seg);
     109              :     });
     110              : 
     111              :   // after iterate the path segments, we should mark functions and associated
     112              :   // functions alive.
     113        45346 :   NodeId ast_node_id = expr.get_mappings ().get_nodeid ();
     114        45346 :   NodeId ref_node_id = UNKNOWN_NODEID;
     115              : 
     116        45346 :   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        45297 :     find_value_definition (ast_node_id, ref_node_id);
     121              : 
     122              :   // node back to HIR
     123        45346 :   tl::optional<HirId> hid = mappings.lookup_node_to_hir (ref_node_id);
     124        45346 :   rust_assert (hid.has_value ());
     125        45346 :   auto ref = hid.value ();
     126              : 
     127              :   // it must resolve to some kind of HIR::Item or HIR::InheritImplItem
     128        45346 :   if (auto resolved_item = mappings.lookup_hir_item (ref))
     129         6557 :     mark_hir_id (resolved_item.value ()->get_mappings ().get_hirid ());
     130        38789 :   else if (auto resolved_item = mappings.lookup_hir_implitem (ref))
     131          487 :     mark_hir_id (resolved_item->first->get_impl_mappings ().get_hirid ());
     132        45346 : }
     133              : 
     134              : void
     135         1597 : MarkLive::visit (HIR::MethodCallExpr &expr)
     136              : {
     137         1597 :   expr.get_receiver ().accept_vis (*this);
     138         1597 :   visit_path_segment (expr.get_method_name ());
     139         2151 :   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         1597 :   NodeId ast_node_id = expr.get_mappings ().get_nodeid ();
     144         1597 :   NodeId ref_node_id = UNKNOWN_NODEID;
     145         1597 :   find_value_definition (ast_node_id, ref_node_id);
     146              : 
     147              :   // node back to HIR
     148         1597 :   if (auto hid = mappings.lookup_node_to_hir (ref_node_id))
     149         1597 :     mark_hir_id (*hid);
     150              :   else
     151            0 :     rust_unreachable ();
     152         1597 : }
     153              : 
     154              : bool
     155        51172 : MarkLive::visit_path_segment (HIR::PathExprSegment seg)
     156              : {
     157        51172 :   NodeId ast_node_id = seg.get_mappings ().get_nodeid ();
     158        51172 :   NodeId ref_node_id = UNKNOWN_NODEID;
     159              : 
     160              :   // There are two different kinds of segment for us.
     161              :   // 1. function segment
     162              :   //      like the symbol "foo" in expression `foo()`.
     163              :   // 2. type segment
     164              :   //      like the symbol "Foo" in expression `Foo{a: 1, b: 2}`
     165              :   //
     166              :   // We should mark them alive all and ignoring other kind of segments.
     167              :   // If the segment we dont care then just return false is fine
     168              :   // TODO: Should we look that up in all namespaces?
     169              : 
     170       102344 :   if (auto nslookup
     171        51172 :       = resolver.lookup (ast_node_id, Resolver2_0::Namespace::Values,
     172        51172 :                          Resolver2_0::Namespace::Types))
     173        48706 :     ref_node_id = nslookup->id;
     174              :   else
     175         2466 :     return false;
     176        48706 :   if (auto hid = mappings.lookup_node_to_hir (ref_node_id))
     177              :     {
     178        48706 :       mark_hir_id (*hid);
     179        48706 :       return true;
     180              :     }
     181            0 :   rust_unreachable ();
     182              : }
     183              : 
     184              : void
     185         3569 : MarkLive::visit (HIR::FieldAccessExpr &expr)
     186              : {
     187              :   // visit receiver at first
     188         3569 :   expr.get_receiver_expr ().accept_vis (*this);
     189              : 
     190              :   // resolve the receiver back to ADT type
     191         3569 :   TyTy::BaseType *receiver = nullptr;
     192         3569 :   if (!tyctx->lookup_type (
     193         3569 :         expr.get_receiver_expr ().get_mappings ().get_hirid (), &receiver))
     194              :     {
     195            0 :       rust_error_at (expr.get_receiver_expr ().get_locus (),
     196              :                      "unresolved type for receiver");
     197              :     }
     198              : 
     199         3569 :   TyTy::ADTType *adt = nullptr;
     200         3569 :   if (receiver->get_kind () == TyTy::TypeKind::ADT)
     201              :     {
     202         3090 :       adt = static_cast<TyTy::ADTType *> (receiver);
     203              : 
     204         3090 :       if (auto inner_ty = TyTy::try_get_box_inner_type (receiver))
     205              :         {
     206            1 :           rust_assert ((*inner_ty)->get_kind () == TyTy::TypeKind::ADT);
     207            1 :           adt = static_cast<TyTy::ADTType *> (*inner_ty);
     208              :         }
     209              :     }
     210          479 :   else if (receiver->get_kind () == TyTy::TypeKind::REF)
     211              :     {
     212          479 :       TyTy::ReferenceType *r = static_cast<TyTy::ReferenceType *> (receiver);
     213          479 :       TyTy::BaseType *b = r->get_base ();
     214          479 :       rust_assert (b->get_kind () == TyTy::TypeKind::ADT);
     215              : 
     216              :       adt = static_cast<TyTy::ADTType *> (b);
     217              :     }
     218              : 
     219         3090 :   rust_assert (adt != nullptr);
     220         3569 :   rust_assert (!adt->is_enum ());
     221         3569 :   rust_assert (adt->number_of_variants () == 1);
     222              : 
     223         3569 :   TyTy::VariantDef *variant = adt->get_variants ().at (0);
     224              : 
     225              :   // get the field index
     226         3569 :   size_t index;
     227         3569 :   TyTy::StructFieldType *field;
     228         3569 :   bool ok = variant->lookup_field (expr.get_field_name ().as_string (), &field,
     229              :                                    &index);
     230         3569 :   rust_assert (ok);
     231         3569 :   if (index >= variant->num_fields ())
     232              :     {
     233            0 :       rust_error_at (expr.get_receiver_expr ().get_locus (),
     234              :                      "cannot access struct %s by index: %lu",
     235            0 :                      adt->get_name ().c_str (), (unsigned long) index);
     236            0 :       return;
     237              :     }
     238              : 
     239              :   // get the field hir id
     240         3569 :   HirId field_id = field->get_ref ();
     241         3569 :   mark_hir_id (field_id);
     242              : }
     243              : 
     244              : void
     245          571 : MarkLive::visit (HIR::TupleIndexExpr &expr)
     246              : {
     247              :   // TODO: unused tuple field detection
     248          571 :   expr.get_tuple_expr ().accept_vis (*this);
     249          571 : }
     250              : 
     251              : void
     252           14 : MarkLive::visit (HIR::TypeAlias &alias)
     253              : {
     254           14 :   NodeId ast_node_id;
     255              : 
     256           14 :   if (auto id = resolver.lookup (
     257           14 :         alias.get_type_aliased ().get_mappings ().get_nodeid (),
     258           14 :         Resolver2_0::Namespace::Types))
     259           14 :     ast_node_id = *id;
     260              :   else
     261            0 :     rust_unreachable ();
     262              : 
     263           14 :   if (auto hid = mappings.lookup_node_to_hir (ast_node_id))
     264           14 :     mark_hir_id (*hid);
     265              :   else
     266            0 :     rust_unreachable ();
     267           14 : }
     268              : 
     269              : void
     270        60930 : MarkLive::mark_hir_id (HirId id)
     271              : {
     272        60930 :   if (scannedSymbols.find (id) == scannedSymbols.end ())
     273              :     {
     274        36713 :       worklist.push_back (id);
     275              :     }
     276        60930 :   liveSymbols.emplace (id);
     277        60930 : }
     278              : 
     279              : void
     280        46894 : MarkLive::find_value_definition (NodeId ast_node_id, NodeId &ref_node_id)
     281              : {
     282        46894 :   auto resolved = resolver.lookup (ast_node_id, Resolver2_0::Namespace::Values,
     283              :                                    Resolver2_0::Namespace::Types);
     284        46894 :   rust_assert (resolved.has_value ());
     285              : 
     286        46894 :   ref_node_id = resolved->id;
     287        46894 : }
     288              : 
     289              : } // namespace Analysis
     290              : } // 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.