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: 88.1 % 135 119
Test Date: 2026-02-28 14:20:25 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         8056 : class FindEntryPoint : public MarkLiveBase
      42              : {
      43              :   using Rust::Analysis::MarkLiveBase::visit;
      44              : 
      45              : public:
      46         4028 :   static std::vector<HirId> find (HIR::Crate &crate)
      47              :   {
      48         8056 :     FindEntryPoint findEntryPoint;
      49        20761 :     for (auto &it : crate.get_items ())
      50        16733 :       it->accept_vis (findEntryPoint);
      51         4028 :     return findEntryPoint.getEntryPoint ();
      52         4028 :   }
      53              : 
      54              :   // TODO not only fn main can be a entry point.
      55         5649 :   void visit (HIR::Function &function) override
      56              :   {
      57         5649 :     if (function.get_function_name ().as_string () == "main")
      58              :       {
      59         7376 :         entryPoints.push_back (function.get_mappings ().get_hirid ());
      60              :       }
      61         5649 :   }
      62              : 
      63              : private:
      64         4028 :   FindEntryPoint () : MarkLiveBase () {}
      65              :   std::vector<HirId> entryPoints;
      66         4028 :   std::vector<HirId> getEntryPoint () { return entryPoints; }
      67              : };
      68              : 
      69              : std::set<HirId>
      70         4028 : MarkLive::Analysis (HIR::Crate &crate)
      71              : {
      72         4028 :   MarkLive marklive (FindEntryPoint::find (crate));
      73         4028 :   marklive.go (crate);
      74              : 
      75         4028 :   return marklive.liveSymbols;
      76         4028 : }
      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         4028 : MarkLive::go (HIR::Crate &)
      85              : {
      86        42327 :   while (!worklist.empty ())
      87              :     {
      88        38299 :       HirId hirId = worklist.back ();
      89        38299 :       worklist.pop_back ();
      90        38299 :       scannedSymbols.emplace (hirId);
      91        38299 :       liveSymbols.emplace (hirId);
      92        38299 :       if (auto item = mappings.lookup_hir_item (hirId))
      93        16100 :         item.value ()->accept_vis (*this);
      94        22199 :       else if (auto implItem = mappings.lookup_hir_implitem (hirId))
      95         1172 :         implItem->first->accept_vis (*this);
      96              :     }
      97         4028 : }
      98              : 
      99              : void
     100        43293 : 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        43293 :   if (!expr.is_lang_item ())
     105        43244 :     expr.iterate_path_segments ([&] (HIR::PathExprSegment &seg) -> bool {
     106        47513 :       return visit_path_segment (seg);
     107              :     });
     108              : 
     109              :   // after iterate the path segments, we should mark functions and associated
     110              :   // functions alive.
     111        43293 :   NodeId ast_node_id = expr.get_mappings ().get_nodeid ();
     112        43293 :   NodeId ref_node_id = UNKNOWN_NODEID;
     113              : 
     114        43293 :   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        43244 :     find_ref_node_id (ast_node_id, ref_node_id);
     119              : 
     120              :   // node back to HIR
     121        43293 :   tl::optional<HirId> hid = mappings.lookup_node_to_hir (ref_node_id);
     122        43293 :   rust_assert (hid.has_value ());
     123        43293 :   auto ref = hid.value ();
     124              : 
     125              :   // it must resolve to some kind of HIR::Item or HIR::InheritImplItem
     126        43293 :   if (auto resolved_item = mappings.lookup_hir_item (ref))
     127         6467 :     mark_hir_id (resolved_item.value ()->get_mappings ().get_hirid ());
     128        36826 :   else if (auto resolved_item = mappings.lookup_hir_implitem (ref))
     129          485 :     mark_hir_id (resolved_item->first->get_impl_mappings ().get_hirid ());
     130        43293 : }
     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        49040 : MarkLive::visit_path_segment (HIR::PathExprSegment seg)
     154              : {
     155        49040 :   NodeId ast_node_id = seg.get_mappings ().get_nodeid ();
     156        49040 :   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        49040 :   if (flag_name_resolution_2_0)
     167              :     {
     168        49040 :       auto &nr_ctx
     169        49040 :         = Resolver2_0::ImmutableNameResolutionContext::get ().resolver ();
     170              : 
     171        49040 :       if (auto id = nr_ctx.lookup (ast_node_id))
     172        46646 :         ref_node_id = *id;
     173              :       else
     174         2394 :         return false;
     175              :     }
     176            0 :   else if (!resolver->lookup_resolved_name (ast_node_id, &ref_node_id))
     177              :     {
     178            0 :       if (!resolver->lookup_resolved_type (ast_node_id, &ref_node_id))
     179              :         return false;
     180              :     }
     181        46646 :   if (auto hid = mappings.lookup_node_to_hir (ref_node_id))
     182              :     {
     183        46646 :       mark_hir_id (*hid);
     184        46646 :       return true;
     185              :     }
     186            0 :   rust_unreachable ();
     187              : }
     188              : 
     189              : void
     190         3422 : MarkLive::visit (HIR::FieldAccessExpr &expr)
     191              : {
     192              :   // visit receiver at first
     193         3422 :   expr.get_receiver_expr ().accept_vis (*this);
     194              : 
     195              :   // resolve the receiver back to ADT type
     196         3422 :   TyTy::BaseType *receiver = nullptr;
     197         3422 :   if (!tyctx->lookup_type (
     198         3422 :         expr.get_receiver_expr ().get_mappings ().get_hirid (), &receiver))
     199              :     {
     200            0 :       rust_error_at (expr.get_receiver_expr ().get_locus (),
     201              :                      "unresolved type for receiver");
     202              :     }
     203              : 
     204         3422 :   TyTy::ADTType *adt = nullptr;
     205         3422 :   if (receiver->get_kind () == TyTy::TypeKind::ADT)
     206              :     {
     207         2964 :       adt = static_cast<TyTy::ADTType *> (receiver);
     208              :     }
     209          458 :   else if (receiver->get_kind () == TyTy::TypeKind::REF)
     210              :     {
     211          458 :       TyTy::ReferenceType *r = static_cast<TyTy::ReferenceType *> (receiver);
     212          458 :       TyTy::BaseType *b = r->get_base ();
     213          458 :       rust_assert (b->get_kind () == TyTy::TypeKind::ADT);
     214              : 
     215              :       adt = static_cast<TyTy::ADTType *> (b);
     216              :     }
     217              : 
     218         2964 :   rust_assert (adt != nullptr);
     219         3422 :   rust_assert (!adt->is_enum ());
     220         3422 :   rust_assert (adt->number_of_variants () == 1);
     221              : 
     222         3422 :   TyTy::VariantDef *variant = adt->get_variants ().at (0);
     223              : 
     224              :   // get the field index
     225         3422 :   size_t index;
     226         3422 :   TyTy::StructFieldType *field;
     227         3422 :   bool ok = variant->lookup_field (expr.get_field_name ().as_string (), &field,
     228              :                                    &index);
     229         3422 :   rust_assert (ok);
     230         3422 :   if (index >= variant->num_fields ())
     231              :     {
     232            0 :       rust_error_at (expr.get_receiver_expr ().get_locus (),
     233              :                      "cannot access struct %s by index: %lu",
     234            0 :                      adt->get_name ().c_str (), (unsigned long) index);
     235            0 :       return;
     236              :     }
     237              : 
     238              :   // get the field hir id
     239         3422 :   HirId field_id = field->get_ref ();
     240         3422 :   mark_hir_id (field_id);
     241              : }
     242              : 
     243              : void
     244          575 : MarkLive::visit (HIR::TupleIndexExpr &expr)
     245              : {
     246              :   // TODO: unused tuple field detection
     247          575 :   expr.get_tuple_expr ().accept_vis (*this);
     248          575 : }
     249              : 
     250              : void
     251           14 : MarkLive::visit (HIR::TypeAlias &alias)
     252              : {
     253           14 :   NodeId ast_node_id = UNKNOWN_NODEID;
     254           14 :   if (flag_name_resolution_2_0)
     255              :     {
     256           14 :       auto &nr_ctx
     257           14 :         = Resolver2_0::ImmutableNameResolutionContext::get ().resolver ();
     258              : 
     259           14 :       if (auto id = nr_ctx.lookup (
     260           28 :             alias.get_type_aliased ().get_mappings ().get_nodeid ()))
     261           14 :         ast_node_id = *id;
     262              :     }
     263              :   else
     264              :     {
     265            0 :       resolver->lookup_resolved_type (
     266            0 :         alias.get_type_aliased ().get_mappings ().get_nodeid (), &ast_node_id);
     267              :     }
     268              : 
     269           14 :   if (auto hid = mappings.lookup_node_to_hir (ast_node_id))
     270           14 :     mark_hir_id (*hid);
     271              :   else
     272            0 :     rust_unreachable ();
     273           14 : }
     274              : 
     275              : void
     276        58561 : MarkLive::mark_hir_id (HirId id)
     277              : {
     278        58561 :   if (scannedSymbols.find (id) == scannedSymbols.end ())
     279              :     {
     280        34611 :       worklist.push_back (id);
     281              :     }
     282        58561 :   liveSymbols.emplace (id);
     283        58561 : }
     284              : 
     285              : void
     286        44771 : MarkLive::find_ref_node_id (NodeId ast_node_id, NodeId &ref_node_id)
     287              : {
     288        44771 :   if (flag_name_resolution_2_0)
     289              :     {
     290        44771 :       auto &nr_ctx
     291        44771 :         = Resolver2_0::ImmutableNameResolutionContext::get ().resolver ();
     292              : 
     293        44771 :       nr_ctx.lookup (ast_node_id).map ([&ref_node_id] (NodeId resolved) {
     294        44771 :         ref_node_id = resolved;
     295              :       });
     296              :     }
     297              :   else
     298              :     {
     299            0 :       if (!resolver->lookup_resolved_name (ast_node_id, &ref_node_id))
     300              :         {
     301            0 :           if (!resolver->lookup_resolved_type (ast_node_id, &ref_node_id))
     302              :             {
     303            0 :               bool ok
     304            0 :                 = resolver->lookup_resolved_misc (ast_node_id, &ref_node_id);
     305            0 :               rust_assert (ok);
     306              :             }
     307              :         }
     308              :     }
     309        44771 : }
     310              : 
     311              : } // namespace Analysis
     312              : } // 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.