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.3 % 119 111
Test Date: 2026-04-20 14:57:17 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-immutable-name-resolution-context.h"
      29              : #include "rust-system.h"
      30              : 
      31              : namespace Rust {
      32              : namespace Analysis {
      33              : 
      34              : // This class trys to find the live symbols which can be used as
      35              : // seeds in MarkLive
      36              : //
      37              : // 1. TODO: explicit live
      38              : //    - Attribute like #[allow(dead_code)]
      39              : //    - Attribute like #[lang=".."], it's not a intra-crate item.
      40              : // 2. TODO: foreign item
      41         8368 : class FindEntryPoint : public MarkLiveBase
      42              : {
      43              :   using Rust::Analysis::MarkLiveBase::visit;
      44              : 
      45              : public:
      46         4184 :   static std::vector<HirId> find (HIR::Crate &crate)
      47              :   {
      48         8368 :     FindEntryPoint findEntryPoint;
      49        21374 :     for (auto &it : crate.get_items ())
      50        17190 :       it->accept_vis (findEntryPoint);
      51         4184 :     return findEntryPoint.getEntryPoint ();
      52         4184 :   }
      53              : 
      54              :   // TODO not only fn main can be a entry point.
      55         5805 :   void visit (HIR::Function &function) override
      56              :   {
      57         5805 :     if (function.get_function_name ().as_string () == "main")
      58              :       {
      59         7672 :         entryPoints.push_back (function.get_mappings ().get_hirid ());
      60              :       }
      61         5805 :   }
      62              : 
      63              : private:
      64         4184 :   FindEntryPoint () : MarkLiveBase () {}
      65              :   std::vector<HirId> entryPoints;
      66         4184 :   std::vector<HirId> getEntryPoint () { return entryPoints; }
      67              : };
      68              : 
      69              : std::set<HirId>
      70         4184 : MarkLive::Analysis (HIR::Crate &crate)
      71              : {
      72         4184 :   MarkLive marklive (FindEntryPoint::find (crate));
      73         4184 :   marklive.go (crate);
      74              : 
      75         4184 :   return marklive.liveSymbols;
      76         4184 : }
      77              : 
      78              : // pop a live symbol from worklist every iteration,
      79              : // if it's a function then walk the function body, and
      80              : // 1. save all the live symbols in worklist which is
      81              : //    visited first time
      82              : // 2. save all the live symbols in liveSymbols
      83              : void
      84         4184 : MarkLive::go (HIR::Crate &)
      85              : {
      86        44204 :   while (!worklist.empty ())
      87              :     {
      88        40020 :       HirId hirId = worklist.back ();
      89        40020 :       worklist.pop_back ();
      90        40020 :       scannedSymbols.emplace (hirId);
      91        40020 :       liveSymbols.emplace (hirId);
      92        40020 :       if (auto item = mappings.lookup_hir_item (hirId))
      93        16278 :         item.value ()->accept_vis (*this);
      94        23742 :       else if (auto implItem = mappings.lookup_hir_implitem (hirId))
      95         1172 :         implItem->first->accept_vis (*this);
      96              :     }
      97         4184 : }
      98              : 
      99              : void
     100        44961 : MarkLive::visit (HIR::PathInExpression &expr)
     101              : {
     102              :   // We should iterate every path segment in order to mark the struct which
     103              :   // is used in expression like Foo::bar(), we should mark the Foo alive.
     104        44961 :   if (!expr.is_lang_item ())
     105        44912 :     expr.iterate_path_segments ([&] (HIR::PathExprSegment &seg) -> bool {
     106        49183 :       return visit_path_segment (seg);
     107              :     });
     108              : 
     109              :   // after iterate the path segments, we should mark functions and associated
     110              :   // functions alive.
     111        44961 :   NodeId ast_node_id = expr.get_mappings ().get_nodeid ();
     112        44961 :   NodeId ref_node_id = UNKNOWN_NODEID;
     113              : 
     114        44961 :   if (expr.is_lang_item ())
     115           49 :     ref_node_id
     116           49 :       = Analysis::Mappings::get ().get_lang_item_node (expr.get_lang_item ());
     117              :   else
     118        44912 :     find_ref_node_id (ast_node_id, ref_node_id);
     119              : 
     120              :   // node back to HIR
     121        44961 :   tl::optional<HirId> hid = mappings.lookup_node_to_hir (ref_node_id);
     122        44961 :   rust_assert (hid.has_value ());
     123        44961 :   auto ref = hid.value ();
     124              : 
     125              :   // it must resolve to some kind of HIR::Item or HIR::InheritImplItem
     126        44961 :   if (auto resolved_item = mappings.lookup_hir_item (ref))
     127         6489 :     mark_hir_id (resolved_item.value ()->get_mappings ().get_hirid ());
     128        38472 :   else if (auto resolved_item = mappings.lookup_hir_implitem (ref))
     129          485 :     mark_hir_id (resolved_item->first->get_impl_mappings ().get_hirid ());
     130        44961 : }
     131              : 
     132              : void
     133         1527 : MarkLive::visit (HIR::MethodCallExpr &expr)
     134              : {
     135         1527 :   expr.get_receiver ().accept_vis (*this);
     136         1527 :   visit_path_segment (expr.get_method_name ());
     137         2060 :   for (auto &argument : expr.get_arguments ())
     138          533 :     argument->accept_vis (*this);
     139              : 
     140              :   // Trying to find the method definition and mark it alive.
     141         1527 :   NodeId ast_node_id = expr.get_mappings ().get_nodeid ();
     142         1527 :   NodeId ref_node_id = UNKNOWN_NODEID;
     143         1527 :   find_ref_node_id (ast_node_id, ref_node_id);
     144              : 
     145              :   // node back to HIR
     146         1527 :   if (auto hid = mappings.lookup_node_to_hir (ref_node_id))
     147         1527 :     mark_hir_id (*hid);
     148              :   else
     149            0 :     rust_unreachable ();
     150         1527 : }
     151              : 
     152              : bool
     153        50710 : MarkLive::visit_path_segment (HIR::PathExprSegment seg)
     154              : {
     155        50710 :   NodeId ast_node_id = seg.get_mappings ().get_nodeid ();
     156        50710 :   NodeId ref_node_id = UNKNOWN_NODEID;
     157              : 
     158              :   // There are two different kinds of segment for us.
     159              :   // 1. function segment
     160              :   //      like the symbol "foo" in expression `foo()`.
     161              :   // 2. type segment
     162              :   //      like the symbol "Foo" in expression `Foo{a: 1, b: 2}`
     163              :   //
     164              :   // We should mark them alive all and ignoring other kind of segments.
     165              :   // If the segment we dont care then just return false is fine
     166        50710 :   if (auto id = resolver.lookup (ast_node_id))
     167        48316 :     ref_node_id = *id;
     168              :   else
     169         2394 :     return false;
     170        48316 :   if (auto hid = mappings.lookup_node_to_hir (ref_node_id))
     171              :     {
     172        48316 :       mark_hir_id (*hid);
     173        48316 :       return true;
     174              :     }
     175            0 :   rust_unreachable ();
     176              : }
     177              : 
     178              : void
     179         3526 : MarkLive::visit (HIR::FieldAccessExpr &expr)
     180              : {
     181              :   // visit receiver at first
     182         3526 :   expr.get_receiver_expr ().accept_vis (*this);
     183              : 
     184              :   // resolve the receiver back to ADT type
     185         3526 :   TyTy::BaseType *receiver = nullptr;
     186         3526 :   if (!tyctx->lookup_type (
     187         3526 :         expr.get_receiver_expr ().get_mappings ().get_hirid (), &receiver))
     188              :     {
     189            0 :       rust_error_at (expr.get_receiver_expr ().get_locus (),
     190              :                      "unresolved type for receiver");
     191              :     }
     192              : 
     193         3526 :   TyTy::ADTType *adt = nullptr;
     194         3526 :   if (receiver->get_kind () == TyTy::TypeKind::ADT)
     195              :     {
     196         3068 :       adt = static_cast<TyTy::ADTType *> (receiver);
     197              :     }
     198          458 :   else if (receiver->get_kind () == TyTy::TypeKind::REF)
     199              :     {
     200          458 :       TyTy::ReferenceType *r = static_cast<TyTy::ReferenceType *> (receiver);
     201          458 :       TyTy::BaseType *b = r->get_base ();
     202          458 :       rust_assert (b->get_kind () == TyTy::TypeKind::ADT);
     203              : 
     204              :       adt = static_cast<TyTy::ADTType *> (b);
     205              :     }
     206              : 
     207         3068 :   rust_assert (adt != nullptr);
     208         3526 :   rust_assert (!adt->is_enum ());
     209         3526 :   rust_assert (adt->number_of_variants () == 1);
     210              : 
     211         3526 :   TyTy::VariantDef *variant = adt->get_variants ().at (0);
     212              : 
     213              :   // get the field index
     214         3526 :   size_t index;
     215         3526 :   TyTy::StructFieldType *field;
     216         3526 :   bool ok = variant->lookup_field (expr.get_field_name ().as_string (), &field,
     217              :                                    &index);
     218         3526 :   rust_assert (ok);
     219         3526 :   if (index >= variant->num_fields ())
     220              :     {
     221            0 :       rust_error_at (expr.get_receiver_expr ().get_locus (),
     222              :                      "cannot access struct %s by index: %lu",
     223            0 :                      adt->get_name ().c_str (), (unsigned long) index);
     224            0 :       return;
     225              :     }
     226              : 
     227              :   // get the field hir id
     228         3526 :   HirId field_id = field->get_ref ();
     229         3526 :   mark_hir_id (field_id);
     230              : }
     231              : 
     232              : void
     233          575 : MarkLive::visit (HIR::TupleIndexExpr &expr)
     234              : {
     235              :   // TODO: unused tuple field detection
     236          575 :   expr.get_tuple_expr ().accept_vis (*this);
     237          575 : }
     238              : 
     239              : void
     240           14 : MarkLive::visit (HIR::TypeAlias &alias)
     241              : {
     242           14 :   NodeId ast_node_id;
     243              : 
     244           14 :   if (auto id = resolver.lookup (
     245           28 :         alias.get_type_aliased ().get_mappings ().get_nodeid ()))
     246           14 :     ast_node_id = *id;
     247              :   else
     248            0 :     rust_unreachable ();
     249              : 
     250           14 :   if (auto hid = mappings.lookup_node_to_hir (ast_node_id))
     251           14 :     mark_hir_id (*hid);
     252              :   else
     253            0 :     rust_unreachable ();
     254           14 : }
     255              : 
     256              : void
     257        60357 : MarkLive::mark_hir_id (HirId id)
     258              : {
     259        60357 :   if (scannedSymbols.find (id) == scannedSymbols.end ())
     260              :     {
     261        36184 :       worklist.push_back (id);
     262              :     }
     263        60357 :   liveSymbols.emplace (id);
     264        60357 : }
     265              : 
     266              : void
     267        46439 : MarkLive::find_ref_node_id (NodeId ast_node_id, NodeId &ref_node_id)
     268              : {
     269        46439 :   auto resolved = resolver.lookup (ast_node_id);
     270        46439 :   rust_assert (resolved.has_value ());
     271        46439 :   ref_node_id = resolved.value ();
     272        46439 : }
     273              : 
     274              : } // namespace Analysis
     275              : } // 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.