Branch data Line data Source code
1 : : // Copyright (C) 2021-2025 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-name-resolver.h"
26 : : #include "rust-immutable-name-resolution-context.h"
27 : :
28 : : namespace Rust {
29 : : namespace Analysis {
30 : :
31 : : // This class trys to find the live symbols which can be used as
32 : : // seeds in MarkLive
33 : : //
34 : : // 1. TODO: explicit live
35 : : // - Attribute like #[allow(dead_code)]
36 : : // - Attribute like #[lang=".."], it's not a intra-crate item.
37 : : // 2. TODO: foreign item
38 : 6580 : class FindEntryPoint : public MarkLiveBase
39 : : {
40 : : using Rust::Analysis::MarkLiveBase::visit;
41 : :
42 : : public:
43 : 3290 : static std::vector<HirId> find (HIR::Crate &crate)
44 : : {
45 : 6580 : FindEntryPoint findEntryPoint;
46 : 15766 : for (auto &it : crate.get_items ())
47 : 12476 : it->accept_vis (findEntryPoint);
48 : 3290 : return findEntryPoint.getEntryPoint ();
49 : 3290 : }
50 : :
51 : : // TODO not only fn main can be a entry point.
52 : 4622 : void visit (HIR::Function &function) override
53 : : {
54 : 4622 : if (function.get_function_name ().as_string () == "main")
55 : : {
56 : 3045 : entryPoints.push_back (function.get_mappings ().get_hirid ());
57 : : }
58 : 4622 : }
59 : :
60 : : private:
61 : 3290 : FindEntryPoint () : MarkLiveBase () {}
62 : : std::vector<HirId> entryPoints;
63 : 3290 : std::vector<HirId> getEntryPoint () { return entryPoints; }
64 : : };
65 : :
66 : : std::set<HirId>
67 : 3290 : MarkLive::Analysis (HIR::Crate &crate)
68 : : {
69 : 3290 : MarkLive marklive (FindEntryPoint::find (crate));
70 : 3290 : marklive.go (crate);
71 : :
72 : 3290 : return marklive.liveSymbols;
73 : 3290 : }
74 : :
75 : : // pop a live symbol from worklist every iteration,
76 : : // if it's a function then walk the function body, and
77 : : // 1. save all the live symbols in worklist which is
78 : : // visited first time
79 : : // 2. save all the live symbols in liveSymbols
80 : : void
81 : 3290 : MarkLive::go (HIR::Crate &)
82 : : {
83 : 31283 : while (!worklist.empty ())
84 : : {
85 : 27993 : HirId hirId = worklist.back ();
86 : 27993 : worklist.pop_back ();
87 : 27993 : scannedSymbols.emplace (hirId);
88 : 27993 : HIR::Item *item = mappings->lookup_hir_item (hirId);
89 : 27993 : liveSymbols.emplace (hirId);
90 : 27993 : if (item != nullptr)
91 : : {
92 : 11319 : item->accept_vis (*this);
93 : : }
94 : : else
95 : : { // the item maybe inside a trait impl
96 : 16674 : HirId parent_impl_id = UNKNOWN_HIRID;
97 : 16674 : HIR::ImplItem *implItem
98 : 16674 : = mappings->lookup_hir_implitem (hirId, &parent_impl_id);
99 : 16674 : if (implItem != nullptr)
100 : 764 : implItem->accept_vis (*this);
101 : : }
102 : : }
103 : 3290 : }
104 : :
105 : : void
106 : 31906 : MarkLive::visit (HIR::PathInExpression &expr)
107 : : {
108 : : // We should iterate every path segment in order to mark the struct which
109 : : // is used in expression like Foo::bar(), we should mark the Foo alive.
110 : 31906 : expr.iterate_path_segments ([&] (HIR::PathExprSegment &seg) -> bool {
111 : 33637 : return visit_path_segment (seg);
112 : : });
113 : :
114 : : // after iterate the path segments, we should mark functions and associated
115 : : // functions alive.
116 : 31906 : NodeId ast_node_id = expr.get_mappings ().get_nodeid ();
117 : 31906 : NodeId ref_node_id = UNKNOWN_NODEID;
118 : 31906 : find_ref_node_id (ast_node_id, ref_node_id);
119 : :
120 : : // node back to HIR
121 : 31906 : HirId ref;
122 : 31906 : bool ok = mappings->lookup_node_to_hir (ref_node_id, &ref);
123 : 31906 : rust_assert (ok);
124 : :
125 : : // it must resolve to some kind of HIR::Item or HIR::InheritImplItem
126 : 31906 : HIR::Item *resolved_item = mappings->lookup_hir_item (ref);
127 : 31906 : if (resolved_item != nullptr)
128 : : {
129 : 4678 : mark_hir_id (resolved_item->get_mappings ().get_hirid ());
130 : : }
131 : : else
132 : : {
133 : 27228 : HirId parent_impl_id = UNKNOWN_HIRID;
134 : 27228 : HIR::ImplItem *resolved_item
135 : 27228 : = mappings->lookup_hir_implitem (ref, &parent_impl_id);
136 : 27228 : if (resolved_item != nullptr)
137 : : {
138 : 407 : mark_hir_id (resolved_item->get_impl_mappings ().get_hirid ());
139 : : }
140 : : }
141 : 31906 : }
142 : :
143 : : void
144 : 791 : MarkLive::visit (HIR::MethodCallExpr &expr)
145 : : {
146 : 791 : expr.get_receiver ()->accept_vis (*this);
147 : 791 : visit_path_segment (expr.get_method_name ());
148 : 1050 : for (auto &argument : expr.get_arguments ())
149 : 259 : argument->accept_vis (*this);
150 : :
151 : : // Trying to find the method definition and mark it alive.
152 : 791 : NodeId ast_node_id = expr.get_mappings ().get_nodeid ();
153 : 791 : NodeId ref_node_id = UNKNOWN_NODEID;
154 : 791 : find_ref_node_id (ast_node_id, ref_node_id);
155 : :
156 : : // node back to HIR
157 : 791 : HirId ref;
158 : 791 : bool ok = mappings->lookup_node_to_hir (ref_node_id, &ref);
159 : 791 : rust_assert (ok);
160 : 791 : mark_hir_id (ref);
161 : 791 : }
162 : :
163 : : bool
164 : 34428 : MarkLive::visit_path_segment (HIR::PathExprSegment seg)
165 : : {
166 : 34428 : NodeId ast_node_id = seg.get_mappings ().get_nodeid ();
167 : 34428 : NodeId ref_node_id = UNKNOWN_NODEID;
168 : :
169 : : // There are two different kinds of segment for us.
170 : : // 1. function segment
171 : : // like the symbol "foo" in expression `foo()`.
172 : : // 2. type segment
173 : : // like the symbol "Foo" in expression `Foo{a: 1, b: 2}`
174 : : //
175 : : // We should mark them alive all and ignoring other kind of segments.
176 : : // If the segment we dont care then just return false is fine
177 : 34428 : if (!resolver->lookup_resolved_name (ast_node_id, &ref_node_id))
178 : : {
179 : 3906 : if (!resolver->lookup_resolved_type (ast_node_id, &ref_node_id))
180 : : return false;
181 : : }
182 : 33054 : HirId ref;
183 : 33054 : bool ok = mappings->lookup_node_to_hir (ref_node_id, &ref);
184 : 33054 : rust_assert (ok);
185 : 33054 : mark_hir_id (ref);
186 : 33054 : return true;
187 : : }
188 : :
189 : : void
190 : 2854 : MarkLive::visit (HIR::FieldAccessExpr &expr)
191 : : {
192 : : // visit receiver at first
193 : 2854 : expr.get_receiver_expr ()->accept_vis (*this);
194 : :
195 : : // resolve the receiver back to ADT type
196 : 2854 : TyTy::BaseType *receiver = nullptr;
197 : 2854 : if (!tyctx->lookup_type (
198 : 2854 : 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 : 2854 : TyTy::ADTType *adt = nullptr;
205 : 2854 : if (receiver->get_kind () == TyTy::TypeKind::ADT)
206 : : {
207 : 2794 : adt = static_cast<TyTy::ADTType *> (receiver);
208 : : }
209 : 60 : else if (receiver->get_kind () == TyTy::TypeKind::REF)
210 : : {
211 : 60 : TyTy::ReferenceType *r = static_cast<TyTy::ReferenceType *> (receiver);
212 : 60 : TyTy::BaseType *b = r->get_base ();
213 : 60 : rust_assert (b->get_kind () == TyTy::TypeKind::ADT);
214 : :
215 : : adt = static_cast<TyTy::ADTType *> (b);
216 : : }
217 : :
218 : 2794 : rust_assert (adt != nullptr);
219 : 2854 : rust_assert (!adt->is_enum ());
220 : 2854 : rust_assert (adt->number_of_variants () == 1);
221 : :
222 : 2854 : TyTy::VariantDef *variant = adt->get_variants ().at (0);
223 : :
224 : : // get the field index
225 : 2854 : size_t index;
226 : 2854 : TyTy::StructFieldType *field;
227 : 2854 : bool ok = variant->lookup_field (expr.get_field_name ().as_string (), &field,
228 : : &index);
229 : 2854 : rust_assert (ok);
230 : 2854 : 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 : 2854 : HirId field_id = field->get_ref ();
240 : 2854 : mark_hir_id (field_id);
241 : : }
242 : :
243 : : void
244 : 509 : MarkLive::visit (HIR::TupleIndexExpr &expr)
245 : : {
246 : : // TODO: unused tuple field detection
247 : 509 : expr.get_tuple_expr ()->accept_vis (*this);
248 : 509 : }
249 : :
250 : : void
251 : 14 : MarkLive::visit (HIR::TypeAlias &alias)
252 : : {
253 : 14 : NodeId ast_node_id;
254 : 14 : resolver->lookup_resolved_type (
255 : 14 : alias.get_type_aliased ()->get_mappings ().get_nodeid (), &ast_node_id);
256 : 14 : HirId hir_id;
257 : 14 : bool ok = mappings->lookup_node_to_hir (ast_node_id, &hir_id);
258 : 14 : rust_assert (ok);
259 : 14 : mark_hir_id (hir_id);
260 : 14 : }
261 : :
262 : : void
263 : 41798 : MarkLive::mark_hir_id (HirId id)
264 : : {
265 : 41798 : if (scannedSymbols.find (id) == scannedSymbols.end ())
266 : : {
267 : 24948 : worklist.push_back (id);
268 : : }
269 : 41798 : liveSymbols.emplace (id);
270 : 41798 : }
271 : :
272 : : void
273 : 32697 : MarkLive::find_ref_node_id (NodeId ast_node_id, NodeId &ref_node_id)
274 : : {
275 : 32697 : if (flag_name_resolution_2_0)
276 : : {
277 : 60 : auto nr_ctx
278 : 60 : = Resolver2_0::ImmutableNameResolutionContext::get ().resolver ();
279 : :
280 : 60 : nr_ctx.lookup (ast_node_id).map ([&ref_node_id] (NodeId resolved) {
281 : 60 : ref_node_id = resolved;
282 : : });
283 : 60 : }
284 : : else
285 : : {
286 : 32637 : if (!resolver->lookup_resolved_name (ast_node_id, &ref_node_id))
287 : : {
288 : 1799 : if (!resolver->lookup_resolved_type (ast_node_id, &ref_node_id))
289 : : {
290 : 2 : bool ok
291 : 2 : = resolver->lookup_resolved_misc (ast_node_id, &ref_node_id);
292 : 2 : rust_assert (ok);
293 : : }
294 : : }
295 : : }
296 : 32697 : }
297 : :
298 : : } // namespace Analysis
299 : : } // namespace Rust
|