]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/rust/checks/lints/rust-lint-marklive.cc
Update copyright years.
[thirdparty/gcc.git] / gcc / rust / checks / lints / rust-lint-marklive.cc
1 // Copyright (C) 2021-2023 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 "rust-hir-full.h"
24 #include "rust-name-resolver.h"
25
26 namespace Rust {
27 namespace Analysis {
28
29 // This class trys to find the live symbols which can be used as
30 // seeds in MarkLive
31 //
32 // 1. TODO: explicit live
33 // - Attribute like #[allow(dead_code)]
34 // - Attribute like #[lang=".."], it's not a intra-crate item.
35 // 2. TODO: foreign item
36 class FindEntryPoint : public MarkLiveBase
37 {
38 using Rust::Analysis::MarkLiveBase::visit;
39
40 public:
41 static std::vector<HirId> find (HIR::Crate &crate)
42 {
43 FindEntryPoint findEntryPoint;
44 for (auto it = crate.items.begin (); it != crate.items.end (); it++)
45 {
46 it->get ()->accept_vis (findEntryPoint);
47 }
48 return findEntryPoint.getEntryPoint ();
49 }
50
51 // TODO not only fn main can be a entry point.
52 void visit (HIR::Function &function) override
53 {
54 if (function.get_function_name () == "main")
55 {
56 entryPoints.push_back (function.get_mappings ().get_hirid ());
57 }
58 }
59
60 private:
61 FindEntryPoint () : MarkLiveBase () {}
62 std::vector<HirId> entryPoints;
63 std::vector<HirId> getEntryPoint () { return entryPoints; }
64 };
65
66 std::set<HirId>
67 MarkLive::Analysis (HIR::Crate &crate)
68 {
69 MarkLive marklive (FindEntryPoint::find (crate));
70 marklive.go (crate);
71
72 return marklive.liveSymbols;
73 }
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 MarkLive::go (HIR::Crate &crate)
82 {
83 while (!worklist.empty ())
84 {
85 HirId hirId = worklist.back ();
86 worklist.pop_back ();
87 scannedSymbols.emplace (hirId);
88 HIR::Item *item = mappings->lookup_hir_item (hirId);
89 liveSymbols.emplace (hirId);
90 if (item != nullptr)
91 {
92 item->accept_vis (*this);
93 }
94 else
95 { // the item maybe inside a trait impl
96 HirId parent_impl_id = UNKNOWN_HIRID;
97 HIR::ImplItem *implItem
98 = mappings->lookup_hir_implitem (hirId, &parent_impl_id);
99 if (implItem != nullptr)
100 implItem->accept_vis (*this);
101 }
102 }
103 }
104
105 void
106 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 expr.iterate_path_segments ([&] (HIR::PathExprSegment &seg) -> bool {
111 return visit_path_segment (seg);
112 });
113
114 // after iterate the path segments, we should mark functions and associated
115 // functions alive.
116 NodeId ast_node_id = expr.get_mappings ().get_nodeid ();
117 NodeId ref_node_id = UNKNOWN_NODEID;
118 find_ref_node_id (ast_node_id, ref_node_id);
119
120 // node back to HIR
121 HirId ref;
122 bool ok = mappings->lookup_node_to_hir (ref_node_id, &ref);
123 rust_assert (ok);
124
125 // it must resolve to some kind of HIR::Item or HIR::InheritImplItem
126 HIR::Item *resolved_item = mappings->lookup_hir_item (ref);
127 if (resolved_item != nullptr)
128 {
129 mark_hir_id (resolved_item->get_mappings ().get_hirid ());
130 }
131 else
132 {
133 HirId parent_impl_id = UNKNOWN_HIRID;
134 HIR::ImplItem *resolved_item
135 = mappings->lookup_hir_implitem (ref, &parent_impl_id);
136 if (resolved_item != nullptr)
137 {
138 mark_hir_id (resolved_item->get_impl_mappings ().get_hirid ());
139 }
140 }
141 }
142
143 void
144 MarkLive::visit (HIR::MethodCallExpr &expr)
145 {
146 expr.get_receiver ()->accept_vis (*this);
147 visit_path_segment (expr.get_method_name ());
148 for (auto &argument : expr.get_arguments ())
149 argument->accept_vis (*this);
150
151 // Trying to find the method definition and mark it alive.
152 NodeId ast_node_id = expr.get_mappings ().get_nodeid ();
153 NodeId ref_node_id = UNKNOWN_NODEID;
154 find_ref_node_id (ast_node_id, ref_node_id);
155
156 // node back to HIR
157 HirId ref;
158 bool ok = mappings->lookup_node_to_hir (ref_node_id, &ref);
159 rust_assert (ok);
160 mark_hir_id (ref);
161 }
162
163 bool
164 MarkLive::visit_path_segment (HIR::PathExprSegment seg)
165 {
166 NodeId ast_node_id = seg.get_mappings ().get_nodeid ();
167 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 if (!resolver->lookup_resolved_name (ast_node_id, &ref_node_id))
178 {
179 if (!resolver->lookup_resolved_type (ast_node_id, &ref_node_id))
180 return false;
181 }
182 HirId ref;
183 bool ok = mappings->lookup_node_to_hir (ref_node_id, &ref);
184 rust_assert (ok);
185 mark_hir_id (ref);
186 return true;
187 }
188
189 void
190 MarkLive::visit (HIR::FieldAccessExpr &expr)
191 {
192 // visit receiver at first
193 expr.get_receiver_expr ()->accept_vis (*this);
194
195 // resolve the receiver back to ADT type
196 TyTy::BaseType *receiver = nullptr;
197 if (!tyctx->lookup_type (
198 expr.get_receiver_expr ()->get_mappings ().get_hirid (), &receiver))
199 {
200 rust_error_at (expr.get_receiver_expr ()->get_locus (),
201 "unresolved type for receiver");
202 }
203
204 TyTy::ADTType *adt = nullptr;
205 if (receiver->get_kind () == TyTy::TypeKind::ADT)
206 {
207 adt = static_cast<TyTy::ADTType *> (receiver);
208 }
209 else if (receiver->get_kind () == TyTy::TypeKind::REF)
210 {
211 TyTy::ReferenceType *r = static_cast<TyTy::ReferenceType *> (receiver);
212 TyTy::BaseType *b = r->get_base ();
213 rust_assert (b->get_kind () == TyTy::TypeKind::ADT);
214
215 adt = static_cast<TyTy::ADTType *> (b);
216 }
217
218 rust_assert (adt != nullptr);
219 rust_assert (!adt->is_enum ());
220 rust_assert (adt->number_of_variants () == 1);
221
222 TyTy::VariantDef *variant = adt->get_variants ().at (0);
223
224 // get the field index
225 size_t index;
226 TyTy::StructFieldType *field;
227 bool ok = variant->lookup_field (expr.get_field_name (), &field, &index);
228 rust_assert (ok);
229 if (index >= variant->num_fields ())
230 {
231 rust_error_at (expr.get_receiver_expr ()->get_locus (),
232 "cannot access struct %s by index: %lu",
233 adt->get_name ().c_str (), (unsigned long) index);
234 return;
235 }
236
237 // get the field hir id
238 HirId field_id = field->get_ref ();
239 mark_hir_id (field_id);
240 }
241
242 void
243 MarkLive::visit (HIR::TupleIndexExpr &expr)
244 {
245 // TODO: unused tuple field detection
246 expr.get_tuple_expr ()->accept_vis (*this);
247 }
248
249 void
250 MarkLive::visit (HIR::TypeAlias &alias)
251 {
252 NodeId ast_node_id;
253 resolver->lookup_resolved_type (
254 alias.get_type_aliased ()->get_mappings ().get_nodeid (), &ast_node_id);
255 HirId hir_id;
256 bool ok = mappings->lookup_node_to_hir (ast_node_id, &hir_id);
257 rust_assert (ok);
258 mark_hir_id (hir_id);
259 }
260
261 void
262 MarkLive::mark_hir_id (HirId id)
263 {
264 if (scannedSymbols.find (id) == scannedSymbols.end ())
265 {
266 worklist.push_back (id);
267 }
268 liveSymbols.emplace (id);
269 }
270
271 void
272 MarkLive::find_ref_node_id (NodeId ast_node_id, NodeId &ref_node_id)
273 {
274 if (!resolver->lookup_resolved_name (ast_node_id, &ref_node_id))
275 {
276 bool ok = resolver->lookup_resolved_type (ast_node_id, &ref_node_id);
277 rust_assert (ok);
278 }
279 }
280
281 } // namespace Analysis
282 } // namespace Rust