Branch data Line data Source code
1 : : // Copyright (C) 2020-2024 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 : : #include "rust-early-name-resolver.h"
20 : : #include "rust-ast-full.h"
21 : : #include "rust-name-resolver.h"
22 : : #include "rust-macro-builtins.h"
23 : : #include "rust-attribute-values.h"
24 : :
25 : : namespace Rust {
26 : : namespace Resolver {
27 : :
28 : : // Check if a module contains the `#[macro_use]` attribute
29 : : static bool
30 : 702 : is_macro_use_module (const AST::Module &mod)
31 : : {
32 : 834 : for (const auto &attr : mod.get_outer_attrs ())
33 : 173 : if (attr.get_path ().as_string () == Values::Attributes::MACRO_USE)
34 : 702 : return true;
35 : :
36 : : return false;
37 : : }
38 : :
39 : : std::vector<std::unique_ptr<AST::Item>>
40 : 702 : EarlyNameResolver::accumulate_escaped_macros (AST::Module &module)
41 : : {
42 : 702 : if (!is_macro_use_module (module))
43 : 661 : return {};
44 : :
45 : : // Parse the module's items if they haven't been expanded and the file
46 : : // should be parsed (i.e isn't hidden behind an untrue or impossible cfg
47 : : // directive)
48 : 41 : if (module.get_kind () == AST::Module::UNLOADED)
49 : 0 : module.load_items ();
50 : :
51 : 41 : std::vector<std::unique_ptr<AST::Item>> escaped_macros;
52 : :
53 : 41 : scoped (module.get_node_id (), [&module, &escaped_macros, this] {
54 : 104 : for (auto &item : module.get_items ())
55 : : {
56 : 63 : if (item->get_ast_kind () == AST::Kind::MODULE)
57 : : {
58 : 13 : auto &module = *static_cast<AST::Module *> (item.get ());
59 : 13 : auto new_macros = accumulate_escaped_macros (module);
60 : :
61 : 13 : std::move (new_macros.begin (), new_macros.end (),
62 : : std::back_inserter (escaped_macros));
63 : :
64 : 13 : continue;
65 : 13 : }
66 : :
67 : 50 : if (item->get_ast_kind () == AST::Kind::MACRO_RULES_DEFINITION)
68 : 48 : escaped_macros.emplace_back (item->clone_item ());
69 : : }
70 : 41 : });
71 : :
72 : 41 : return escaped_macros;
73 : 41 : }
74 : :
75 : 5245 : EarlyNameResolver::EarlyNameResolver ()
76 : 5245 : : current_scope (UNKNOWN_NODEID), resolver (*Resolver::get ()),
77 : 5245 : mappings (*Analysis::Mappings::get ())
78 : 5245 : {}
79 : :
80 : : void
81 : 5245 : EarlyNameResolver::go (AST::Crate &crate)
82 : : {
83 : 5245 : visit (crate);
84 : 5245 : }
85 : :
86 : : void
87 : 1648 : EarlyNameResolver::resolve_generic_args (AST::GenericArgs &generic_args)
88 : : {
89 : 3448 : for (auto &arg : generic_args.get_generic_args ())
90 : 1800 : arg.accept_vis (*this);
91 : :
92 : 1650 : for (auto &arg : generic_args.get_binding_args ())
93 : 2 : arg.get_type ().accept_vis (*this);
94 : 1648 : }
95 : :
96 : : void
97 : 310 : EarlyNameResolver::resolve_qualified_path_type (AST::QualifiedPathType &path)
98 : : {
99 : 310 : path.get_type ().accept_vis (*this);
100 : :
101 : 310 : if (path.has_as_clause ())
102 : 285 : path.get_as_type_path ().accept_vis (*this);
103 : 310 : }
104 : :
105 : : void
106 : 5245 : EarlyNameResolver::visit (AST::Crate &crate)
107 : : {
108 : 5245 : std::vector<std::unique_ptr<AST::Item>> new_items;
109 : 5245 : auto items = crate.take_items ();
110 : :
111 : 5245 : scoped (crate.get_node_id (), [&items, &new_items, this] {
112 : 24528 : for (auto &&item : items)
113 : : {
114 : 19283 : auto new_macros = std::vector<std::unique_ptr<AST::Item>> ();
115 : :
116 : 19283 : if (item->get_ast_kind () == AST::Kind::MODULE)
117 : 1080 : new_macros = accumulate_escaped_macros (
118 : 1080 : *static_cast<AST::Module *> (item.get ()));
119 : :
120 : 19283 : new_items.emplace_back (std::move (item));
121 : 19283 : std::move (new_macros.begin (), new_macros.end (),
122 : : std::back_inserter (new_items));
123 : 19283 : }
124 : 5245 : });
125 : :
126 : 5245 : crate.set_items (std::move (new_items));
127 : :
128 : 5245 : scoped (crate.get_node_id (), [&crate, this] () {
129 : 24566 : for (auto &item : crate.items)
130 : 19321 : item->accept_vis (*this);
131 : 5245 : });
132 : 5245 : }
133 : :
134 : : void
135 : 0 : EarlyNameResolver::visit (AST::DelimTokenTree &)
136 : 0 : {}
137 : :
138 : : void
139 : 0 : EarlyNameResolver::visit (AST::AttrInputMetaItemContainer &)
140 : 0 : {}
141 : :
142 : : void
143 : 17163 : EarlyNameResolver::visit (AST::IdentifierExpr &)
144 : 17163 : {}
145 : :
146 : : void
147 : 208 : EarlyNameResolver::visit (AST::LifetimeParam &)
148 : 208 : {}
149 : :
150 : : void
151 : 26 : EarlyNameResolver::visit (AST::ConstGenericParam &)
152 : 26 : {}
153 : :
154 : : // FIXME: ARTHUR: Do we need to perform macro resolution for paths as well?
155 : : // std::arch::asm!()?
156 : : void
157 : 12552 : EarlyNameResolver::visit (AST::PathInExpression &path)
158 : : {
159 : 28074 : for (auto &segment : path.get_segments ())
160 : 31044 : if (segment.has_generic_args ())
161 : 392 : resolve_generic_args (segment.get_generic_args ());
162 : 12552 : }
163 : :
164 : : void
165 : 1256 : EarlyNameResolver::visit (AST::TypePathSegmentGeneric &segment)
166 : : {
167 : 1256 : if (segment.has_generic_args ())
168 : 1256 : resolve_generic_args (segment.get_generic_args ());
169 : 1256 : }
170 : :
171 : : void
172 : 88 : EarlyNameResolver::visit (AST::QualifiedPathInExpression &path)
173 : : {
174 : 88 : resolve_qualified_path_type (path.get_qualified_path_type ());
175 : :
176 : 176 : for (auto &segment : path.get_segments ())
177 : 176 : if (segment.has_generic_args ())
178 : 0 : resolve_generic_args (segment.get_generic_args ());
179 : 88 : }
180 : :
181 : : void
182 : 222 : EarlyNameResolver::visit (AST::QualifiedPathInType &path)
183 : : {
184 : 222 : resolve_qualified_path_type (path.get_qualified_path_type ());
185 : :
186 : 222 : for (auto &segment : path.get_segments ())
187 : 0 : segment->accept_vis (*this);
188 : 222 : }
189 : :
190 : : void
191 : 538529 : EarlyNameResolver::visit (AST::LiteralExpr &)
192 : 538529 : {}
193 : :
194 : : void
195 : 0 : EarlyNameResolver::visit (AST::AttrInputLiteral &)
196 : 0 : {}
197 : :
198 : : void
199 : 0 : EarlyNameResolver::visit (AST::AttrInputMacro &)
200 : 0 : {}
201 : :
202 : : void
203 : 0 : EarlyNameResolver::visit (AST::MetaItemLitExpr &)
204 : 0 : {}
205 : :
206 : : void
207 : 0 : EarlyNameResolver::visit (AST::MetaItemPathLit &)
208 : 0 : {}
209 : :
210 : : void
211 : 50 : EarlyNameResolver::visit (AST::StructExprStruct &)
212 : 50 : {}
213 : :
214 : : void
215 : 211 : EarlyNameResolver::visit (AST::StructExprFieldIdentifier &)
216 : 211 : {}
217 : :
218 : : void
219 : 0 : EarlyNameResolver::visit (AST::StructExprStructBase &)
220 : 0 : {}
221 : :
222 : : void
223 : 16857 : EarlyNameResolver::visit (AST::BlockExpr &expr)
224 : : {
225 : 16857 : scoped (expr.get_node_id (), [&expr, this] () {
226 : 35764 : for (auto &stmt : expr.get_statements ())
227 : 18907 : stmt->accept_vis (*this);
228 : :
229 : 16857 : if (expr.has_tail_expr ())
230 : 11876 : expr.get_tail_expr ().accept_vis (*this);
231 : 16857 : });
232 : 16857 : }
233 : :
234 : : void
235 : 10 : EarlyNameResolver::visit (AST::ContinueExpr &)
236 : 10 : {}
237 : :
238 : : void
239 : 0 : EarlyNameResolver::visit (AST::RangeFullExpr &)
240 : 0 : {}
241 : :
242 : : void
243 : 0 : EarlyNameResolver::visit (AST::ForLoopExpr &expr)
244 : : {
245 : 0 : scoped (expr.get_node_id (), [&expr, this] () {
246 : 0 : expr.get_pattern ().accept_vis (*this);
247 : 0 : expr.get_iterator_expr ().accept_vis (*this);
248 : 0 : expr.get_loop_block ().accept_vis (*this);
249 : 0 : });
250 : 0 : }
251 : :
252 : : void
253 : 3 : EarlyNameResolver::visit (AST::IfLetExpr &expr)
254 : : {
255 : 3 : expr.get_value_expr ().accept_vis (*this);
256 : :
257 : 3 : scoped (expr.get_node_id (),
258 : 6 : [&expr, this] () { expr.get_if_block ().accept_vis (*this); });
259 : 3 : }
260 : :
261 : : void
262 : 240 : EarlyNameResolver::visit (AST::MatchExpr &expr)
263 : : {
264 : 240 : expr.get_scrutinee_expr ().accept_vis (*this);
265 : :
266 : 240 : scoped (expr.get_node_id (), [&expr, this] () {
267 : 821 : for (auto &arm : expr.get_match_cases ())
268 : : {
269 : 581 : scoped (arm.get_node_id (), [&arm, this] () {
270 : 581 : if (arm.get_arm ().has_match_arm_guard ())
271 : 2 : arm.get_arm ().get_guard_expr ().accept_vis (*this);
272 : :
273 : 1162 : for (auto &pattern : arm.get_arm ().get_patterns ())
274 : 581 : pattern->accept_vis (*this);
275 : :
276 : 581 : arm.get_expr ().accept_vis (*this);
277 : 581 : });
278 : : }
279 : 240 : });
280 : 240 : }
281 : :
282 : : void
283 : 0 : EarlyNameResolver::visit (AST::LifetimeWhereClauseItem &)
284 : 0 : {}
285 : :
286 : : void
287 : 689 : EarlyNameResolver::visit (AST::Module &module)
288 : : {
289 : 689 : if (module.get_kind () == AST::Module::UNLOADED)
290 : 63 : module.load_items ();
291 : :
292 : : // so we need to only go "one scope down" for fetching macros. Macros within
293 : : // functions are still scoped only within that function. But we have to be
294 : : // careful because nested modules with #[macro_use] actually works!
295 : 689 : std::vector<std::unique_ptr<AST::Item>> new_items;
296 : 689 : auto items = module.take_items ();
297 : :
298 : 689 : scoped (module.get_node_id (), [&items, &new_items, this] {
299 : 1845 : for (auto &&item : items)
300 : : {
301 : 1156 : auto new_macros = std::vector<std::unique_ptr<AST::Item>> ();
302 : :
303 : 1156 : if (item->get_ast_kind () == AST::Kind::MODULE)
304 : 298 : new_macros = accumulate_escaped_macros (
305 : 298 : *static_cast<AST::Module *> (item.get ()));
306 : :
307 : 1156 : new_items.emplace_back (std::move (item));
308 : 1156 : std::move (new_macros.begin (), new_macros.end (),
309 : : std::back_inserter (new_items));
310 : 1156 : }
311 : 689 : });
312 : :
313 : 689 : module.set_items (std::move (new_items));
314 : :
315 : 689 : scoped (module.get_node_id (), [&module, this] () {
316 : 1855 : for (auto &item : module.get_items ())
317 : 1166 : item->accept_vis (*this);
318 : 689 : });
319 : 689 : }
320 : :
321 : : void
322 : 8 : EarlyNameResolver::visit (AST::ExternCrate &)
323 : 8 : {}
324 : :
325 : : void
326 : 0 : EarlyNameResolver::visit (AST::UseTreeGlob &)
327 : 0 : {}
328 : :
329 : : void
330 : 0 : EarlyNameResolver::visit (AST::UseTreeList &)
331 : 0 : {}
332 : :
333 : : void
334 : 0 : EarlyNameResolver::visit (AST::UseTreeRebind &)
335 : 0 : {}
336 : :
337 : : void
338 : 159 : EarlyNameResolver::visit (AST::UseDeclaration &)
339 : 159 : {}
340 : :
341 : : void
342 : 535 : EarlyNameResolver::visit (AST::EnumItem &)
343 : 535 : {}
344 : :
345 : : void
346 : 94 : EarlyNameResolver::visit (AST::Union &)
347 : 94 : {}
348 : :
349 : : void
350 : 581 : EarlyNameResolver::visit (AST::TraitItemType &)
351 : 581 : {}
352 : :
353 : : void
354 : 2431 : EarlyNameResolver::visit (AST::Trait &trait)
355 : : {
356 : 2817 : for (auto &generic : trait.get_generic_params ())
357 : 386 : generic->accept_vis (*this);
358 : :
359 : 2431 : scoped (trait.get_node_id (), [&trait, this] () {
360 : 4413 : for (auto &item : trait.get_trait_items ())
361 : 1982 : item->accept_vis (*this);
362 : 2431 : });
363 : 2431 : }
364 : :
365 : : void
366 : 790 : EarlyNameResolver::visit (AST::InherentImpl &impl)
367 : : {
368 : 790 : impl.get_type ().accept_vis (*this);
369 : :
370 : 1061 : for (auto &generic : impl.get_generic_params ())
371 : 271 : generic->accept_vis (*this);
372 : :
373 : 790 : scoped (impl.get_node_id (), [&impl, this] () {
374 : 2907 : for (auto &item : impl.get_impl_items ())
375 : 2117 : item->accept_vis (*this);
376 : 790 : });
377 : 790 : }
378 : :
379 : : void
380 : 2851 : EarlyNameResolver::visit (AST::TraitImpl &impl)
381 : : {
382 : 2851 : impl.get_type ().accept_vis (*this);
383 : :
384 : 3533 : for (auto &generic : impl.get_generic_params ())
385 : 682 : generic->accept_vis (*this);
386 : :
387 : 2851 : scoped (impl.get_node_id (), [&impl, this] () {
388 : 6032 : for (auto &item : impl.get_impl_items ())
389 : 3181 : item->accept_vis (*this);
390 : 2851 : });
391 : 2851 : }
392 : :
393 : : void
394 : 1 : EarlyNameResolver::visit (AST::ExternalTypeItem &item)
395 : : {
396 : : // nothing to do?
397 : 1 : }
398 : :
399 : : void
400 : 1273 : EarlyNameResolver::visit (AST::ExternBlock &block)
401 : : {
402 : 1273 : scoped (block.get_node_id (), [&block, this] () {
403 : 3363 : for (auto &item : block.get_extern_items ())
404 : 2090 : item->accept_vis (*this);
405 : 1273 : });
406 : 1273 : }
407 : :
408 : : void
409 : 0 : EarlyNameResolver::visit (AST::MacroMatchRepetition &)
410 : 0 : {}
411 : :
412 : : void
413 : 0 : EarlyNameResolver::visit (AST::MacroMatcher &)
414 : 0 : {}
415 : :
416 : : void
417 : 2585 : EarlyNameResolver::visit (AST::MacroRulesDefinition &rules_def)
418 : : {
419 : 2585 : auto path = CanonicalPath::new_seg (rules_def.get_node_id (),
420 : 2585 : rules_def.get_rule_name ().as_string ());
421 : 2585 : resolver.get_macro_scope ().insert (path, rules_def.get_node_id (),
422 : : rules_def.get_locus ());
423 : :
424 : : /* Since the EarlyNameResolver runs multiple time (fixed point algorithm)
425 : : * we could be inserting the same macro def over and over again until we
426 : : * implement some optimizations */
427 : : // FIXME: ARTHUR: Remove that lookup and add proper optimizations instead
428 : 2585 : AST::MacroRulesDefinition *tmp = nullptr;
429 : 2585 : if (mappings.lookup_macro_def (rules_def.get_node_id (), &tmp))
430 : 1866 : return;
431 : :
432 : 719 : mappings.insert_macro_def (&rules_def);
433 : 719 : rust_debug_loc (rules_def.get_locus (), "inserting macro def: [%s]",
434 : 1438 : path.get ().c_str ());
435 : 2585 : }
436 : :
437 : : void
438 : 2263 : EarlyNameResolver::visit (AST::MacroInvocation &invoc)
439 : : {
440 : 2263 : auto &invoc_data = invoc.get_invoc_data ();
441 : 2263 : auto has_semicolon = invoc.has_semicolon ();
442 : :
443 : 2263 : if (invoc.get_kind () == AST::MacroInvocation::InvocKind::Builtin)
444 : 13 : for (auto &pending_invoc : invoc.get_pending_eager_invocations ())
445 : 8 : pending_invoc->accept_vis (*this);
446 : :
447 : : // ??
448 : : // switch on type of macro:
449 : : // - '!' syntax macro (inner switch)
450 : : // - procedural macro - "A token-based function-like macro"
451 : : // - 'macro_rules' (by example/pattern-match) macro? or not? "an
452 : : // AST-based function-like macro"
453 : : // - else is unreachable
454 : : // - attribute syntax macro (inner switch)
455 : : // - procedural macro attribute syntax - "A token-based attribute
456 : : // macro"
457 : : // - legacy macro attribute syntax? - "an AST-based attribute macro"
458 : : // - non-macro attribute: mark known
459 : : // - else is unreachable
460 : : // - derive macro (inner switch)
461 : : // - derive or legacy derive - "token-based" vs "AST-based"
462 : : // - else is unreachable
463 : : // - derive container macro - unreachable
464 : :
465 : : // lookup the rules for this macro
466 : 2263 : NodeId resolved_node = UNKNOWN_NODEID;
467 : 2263 : NodeId source_node = UNKNOWN_NODEID;
468 : 2263 : if (has_semicolon)
469 : 559 : source_node = invoc.get_macro_node_id ();
470 : : else
471 : 1704 : source_node = invoc.get_node_id ();
472 : 2263 : auto seg
473 : 2263 : = CanonicalPath::new_seg (source_node, invoc_data.get_path ().as_string ());
474 : :
475 : 2263 : bool found = resolver.get_macro_scope ().lookup (seg, &resolved_node);
476 : 2263 : if (!found)
477 : : {
478 : 8 : rust_error_at (invoc.get_locus (), "unknown macro: [%s]",
479 : 8 : seg.get ().c_str ());
480 : 8 : return;
481 : : }
482 : :
483 : : // lookup the rules
484 : 2255 : AST::MacroRulesDefinition *rules_def = nullptr;
485 : 2255 : bool ok = mappings.lookup_macro_def (resolved_node, &rules_def);
486 : 2255 : rust_assert (ok);
487 : :
488 : 2255 : auto &outer_attrs = rules_def->get_outer_attrs ();
489 : 2255 : bool is_builtin
490 : 2255 : = std::any_of (outer_attrs.begin (), outer_attrs.end (),
491 : 218 : [] (AST::Attribute attr) {
492 : 218 : return attr.get_path ()
493 : 218 : == Values::Attributes::RUSTC_BUILTIN_MACRO;
494 : : });
495 : :
496 : 2255 : if (is_builtin)
497 : : {
498 : 218 : auto builtin_kind
499 : 218 : = builtin_macro_from_string (rules_def->get_rule_name ().as_string ());
500 : 218 : invoc.map_to_builtin (builtin_kind.value ());
501 : : }
502 : :
503 : 2255 : auto attributes = rules_def->get_outer_attrs ();
504 : :
505 : : /* Since the EarlyNameResolver runs multiple time (fixed point algorithm)
506 : : * we could be inserting the same macro def over and over again until we
507 : : * implement some optimizations */
508 : : // FIXME: ARTHUR: Remove that lookup and add proper optimizations instead
509 : 2255 : AST::MacroRulesDefinition *tmp_def = nullptr;
510 : 2255 : if (mappings.lookup_macro_invocation (invoc, &tmp_def))
511 : 0 : return;
512 : :
513 : 2255 : mappings.insert_macro_invocation (invoc, rules_def);
514 : 2263 : }
515 : :
516 : : // FIXME: ARTHUR: Do we need to resolve these as well here?
517 : :
518 : : void
519 : 0 : EarlyNameResolver::visit (AST::MetaItemPath &)
520 : 0 : {}
521 : :
522 : : void
523 : 0 : EarlyNameResolver::visit (AST::MetaItemSeq &)
524 : 0 : {}
525 : :
526 : : void
527 : 0 : EarlyNameResolver::visit (AST::MetaNameValueStr &)
528 : 0 : {}
529 : :
530 : : void
531 : 0 : EarlyNameResolver::visit (AST::MetaListPaths &)
532 : 0 : {}
533 : :
534 : : void
535 : 0 : EarlyNameResolver::visit (AST::MetaListNameValueStr &)
536 : 0 : {}
537 : :
538 : : void
539 : 21 : EarlyNameResolver::visit (AST::RangePatternBoundLiteral &)
540 : 21 : {}
541 : :
542 : : void
543 : 21 : EarlyNameResolver::visit (AST::RangePatternBoundPath &)
544 : 21 : {}
545 : :
546 : : void
547 : 0 : EarlyNameResolver::visit (AST::RangePatternBoundQualPath &)
548 : 0 : {}
549 : :
550 : : void
551 : 0 : EarlyNameResolver::visit (AST::StructPatternFieldIdent &)
552 : 0 : {}
553 : :
554 : : void
555 : 35 : EarlyNameResolver::visit (AST::StructPattern &)
556 : 35 : {}
557 : :
558 : : void
559 : 237 : EarlyNameResolver::visit (AST::TupleStructPattern &pattern)
560 : : {
561 : 237 : pattern.get_items ().accept_vis (*this);
562 : 237 : }
563 : :
564 : : void
565 : 390 : EarlyNameResolver::visit (AST::TraitBound &)
566 : 390 : {}
567 : :
568 : : void
569 : 0 : EarlyNameResolver::visit (AST::ImplTraitType &)
570 : 0 : {}
571 : :
572 : : void
573 : 7 : EarlyNameResolver::visit (AST::TraitObjectType &)
574 : 7 : {}
575 : :
576 : : void
577 : 0 : EarlyNameResolver::visit (AST::ParenthesisedType &)
578 : 0 : {}
579 : :
580 : : void
581 : 0 : EarlyNameResolver::visit (AST::ImplTraitTypeOneBound &)
582 : 0 : {}
583 : :
584 : : void
585 : 14 : EarlyNameResolver::visit (AST::TraitObjectTypeOneBound &)
586 : 14 : {}
587 : :
588 : : void
589 : 294 : EarlyNameResolver::visit (AST::TupleType &)
590 : 294 : {}
591 : :
592 : : void
593 : 5669 : EarlyNameResolver::visit (AST::RawPointerType &)
594 : 5669 : {}
595 : :
596 : : void
597 : 1950 : EarlyNameResolver::visit (AST::ReferenceType &)
598 : 1950 : {}
599 : :
600 : : void
601 : 457 : EarlyNameResolver::visit (AST::ArrayType &)
602 : 457 : {}
603 : :
604 : : void
605 : 103 : EarlyNameResolver::visit (AST::SliceType &)
606 : 103 : {}
607 : :
608 : : void
609 : 58 : EarlyNameResolver::visit (AST::InferredType &)
610 : 58 : {}
611 : :
612 : : } // namespace Resolver
613 : : } // namespace Rust
|