blob: 6dd6349dab1f4fa897d1ec04d32c60c99d509f90 [file] [log] [blame]
David Tolnaycec98a72019-05-08 15:23:19 -07001use crate::file;
David Tolnay397bd0b2019-02-15 20:51:10 -08002use quote::quote;
David Tolnay950cc122019-05-07 14:21:13 -07003use syn_codegen as types;
Carl Lerche058ff472019-02-13 16:23:52 -08004
Carl Lerche058ff472019-02-13 16:23:52 -08005const VISIT_SRC: &str = "../src/gen/visit.rs";
6const VISIT_MUT_SRC: &str = "../src/gen/visit_mut.rs";
7
8mod codegen {
Carl Lerche058ff472019-02-13 16:23:52 -08009 use inflections::Inflect;
10 use proc_macro2::{Span, TokenStream};
David Tolnay397bd0b2019-02-15 20:51:10 -080011 use quote::{quote, TokenStreamExt};
Carl Lerche058ff472019-02-13 16:23:52 -080012 use syn::*;
David Tolnay950cc122019-05-07 14:21:13 -070013 use syn_codegen as types;
Carl Lerche058ff472019-02-13 16:23:52 -080014
15 #[derive(Default)]
16 pub struct State {
17 pub visit_trait: TokenStream,
18 pub visit_impl: TokenStream,
19 pub visit_mut_trait: TokenStream,
20 pub visit_mut_impl: TokenStream,
Carl Lerche058ff472019-02-13 16:23:52 -080021 }
22
23 fn under_name(name: &str) -> Ident {
24 Ident::new(&name.to_snake_case(), Span::call_site())
25 }
26
27 #[derive(Debug, Eq, PartialEq, Copy, Clone)]
28 enum Kind {
29 Visit,
30 VisitMut,
Carl Lerche058ff472019-02-13 16:23:52 -080031 }
32
33 enum Operand {
34 Borrowed(TokenStream),
35 Owned(TokenStream),
36 }
37
38 use self::Kind::*;
39 use self::Operand::*;
40
41 impl Operand {
42 fn tokens(&self) -> &TokenStream {
43 match *self {
44 Borrowed(ref n) | Owned(ref n) => n,
45 }
46 }
47
48 fn ref_tokens(&self) -> TokenStream {
49 match *self {
50 Borrowed(ref n) => n.clone(),
51 Owned(ref n) => quote!(&#n),
52 }
53 }
54
55 fn ref_mut_tokens(&self) -> TokenStream {
56 match *self {
57 Borrowed(ref n) => n.clone(),
58 Owned(ref n) => quote!(&mut #n),
59 }
60 }
61
62 fn owned_tokens(&self) -> TokenStream {
63 match *self {
64 Borrowed(ref n) => quote!(*#n),
65 Owned(ref n) => n.clone(),
66 }
67 }
68 }
69
70 fn simple_visit(item: &str, kind: Kind, name: &Operand) -> TokenStream {
71 let ident = under_name(item);
72
73 match kind {
74 Visit => {
75 let method = Ident::new(&format!("visit_{}", ident), Span::call_site());
76 let name = name.ref_tokens();
77 quote! {
78 _visitor.#method(#name)
79 }
80 }
81 VisitMut => {
82 let method = Ident::new(&format!("visit_{}_mut", ident), Span::call_site());
83 let name = name.ref_mut_tokens();
84 quote! {
85 _visitor.#method(#name)
86 }
87 }
Carl Lerche058ff472019-02-13 16:23:52 -080088 }
89 }
90
91 fn box_visit(
92 elem: &types::Type,
93 features: &types::Features,
David Tolnay157c7eb2019-02-15 13:21:48 -080094 defs: &types::Definitions,
Carl Lerche058ff472019-02-13 16:23:52 -080095 kind: Kind,
96 name: &Operand,
97 ) -> Option<TokenStream> {
98 let name = name.owned_tokens();
David Tolnay157c7eb2019-02-15 13:21:48 -080099 let res = visit(elem, features, defs, kind, &Owned(quote!(*#name)))?;
David Tolnayf1e57f02019-05-08 15:30:39 -0700100 Some(res)
Carl Lerche058ff472019-02-13 16:23:52 -0800101 }
102
103 fn vec_visit(
104 elem: &types::Type,
105 features: &types::Features,
David Tolnay157c7eb2019-02-15 13:21:48 -0800106 defs: &types::Definitions,
Carl Lerche058ff472019-02-13 16:23:52 -0800107 kind: Kind,
108 name: &Operand,
109 ) -> Option<TokenStream> {
David Tolnayf1e57f02019-05-08 15:30:39 -0700110 let operand = Borrowed(quote!(it));
David Tolnay157c7eb2019-02-15 13:21:48 -0800111 let val = visit(elem, features, defs, kind, &operand)?;
Carl Lerche058ff472019-02-13 16:23:52 -0800112 Some(match kind {
113 Visit => {
114 let name = name.ref_tokens();
115 quote! {
116 for it in #name {
117 #val
118 }
119 }
120 }
121 VisitMut => {
122 let name = name.ref_mut_tokens();
123 quote! {
124 for it in #name {
125 #val
126 }
127 }
128 }
Carl Lerche058ff472019-02-13 16:23:52 -0800129 })
130 }
131
132 fn punctuated_visit(
133 elem: &types::Type,
134 features: &types::Features,
David Tolnay157c7eb2019-02-15 13:21:48 -0800135 defs: &types::Definitions,
Carl Lerche058ff472019-02-13 16:23:52 -0800136 kind: Kind,
137 name: &Operand,
138 ) -> Option<TokenStream> {
David Tolnayf1e57f02019-05-08 15:30:39 -0700139 let operand = Borrowed(quote!(it));
David Tolnay157c7eb2019-02-15 13:21:48 -0800140 let val = visit(elem, features, defs, kind, &operand)?;
Carl Lerche058ff472019-02-13 16:23:52 -0800141 Some(match kind {
142 Visit => {
143 let name = name.ref_tokens();
144 quote! {
145 for el in Punctuated::pairs(#name) {
146 let it = el.value();
147 #val
148 }
149 }
150 }
151 VisitMut => {
152 let name = name.ref_mut_tokens();
153 quote! {
154 for mut el in Punctuated::pairs_mut(#name) {
155 let it = el.value_mut();
156 #val
157 }
158 }
159 }
Carl Lerche058ff472019-02-13 16:23:52 -0800160 })
161 }
162
163 fn option_visit(
164 elem: &types::Type,
165 features: &types::Features,
David Tolnay157c7eb2019-02-15 13:21:48 -0800166 defs: &types::Definitions,
Carl Lerche058ff472019-02-13 16:23:52 -0800167 kind: Kind,
168 name: &Operand,
169 ) -> Option<TokenStream> {
David Tolnayf1e57f02019-05-08 15:30:39 -0700170 let it = Borrowed(quote!(it));
David Tolnay157c7eb2019-02-15 13:21:48 -0800171 let val = visit(elem, features, defs, kind, &it)?;
Carl Lerche058ff472019-02-13 16:23:52 -0800172 let name = name.owned_tokens();
173 Some(match kind {
174 Visit => quote! {
175 if let Some(ref it) = #name {
176 #val
177 }
178 },
179 VisitMut => quote! {
180 if let Some(ref mut it) = #name {
181 #val
182 }
183 },
Carl Lerche058ff472019-02-13 16:23:52 -0800184 })
185 }
186
187 fn tuple_visit(
188 elems: &[types::Type],
189 features: &types::Features,
David Tolnay157c7eb2019-02-15 13:21:48 -0800190 defs: &types::Definitions,
Carl Lerche058ff472019-02-13 16:23:52 -0800191 kind: Kind,
192 name: &Operand,
193 ) -> Option<TokenStream> {
194 if elems.is_empty() {
195 return None;
196 }
197
198 let mut code = TokenStream::new();
199 for (i, elem) in elems.iter().enumerate() {
200 let name = name.tokens();
201 let i = Index::from(i);
202 let it = Owned(quote!((#name).#i));
David Tolnayf1e57f02019-05-08 15:30:39 -0700203 let val = visit(elem, features, defs, kind, &it).unwrap_or_else(|| noop_visit(&it));
Carl Lerche058ff472019-02-13 16:23:52 -0800204 code.append_all(val);
David Tolnayf1e57f02019-05-08 15:30:39 -0700205 code.append_all(quote!(;));
Carl Lerche058ff472019-02-13 16:23:52 -0800206 }
David Tolnayf1e57f02019-05-08 15:30:39 -0700207 Some(code)
Carl Lerche058ff472019-02-13 16:23:52 -0800208 }
209
David Tolnayf1e57f02019-05-08 15:30:39 -0700210 fn token_punct_visit(kind: Kind, name: &Operand) -> TokenStream {
Carl Lerche058ff472019-02-13 16:23:52 -0800211 let name = name.tokens();
212 match kind {
Carl Lerche058ff472019-02-13 16:23:52 -0800213 Visit => quote! {
214 tokens_helper(_visitor, &#name.spans)
215 },
216 VisitMut => quote! {
217 tokens_helper(_visitor, &mut #name.spans)
218 },
219 }
220 }
221
David Tolnayf1e57f02019-05-08 15:30:39 -0700222 fn token_keyword_visit(kind: Kind, name: &Operand) -> TokenStream {
Carl Lerche058ff472019-02-13 16:23:52 -0800223 let name = name.tokens();
224 match kind {
Carl Lerche058ff472019-02-13 16:23:52 -0800225 Visit => quote! {
226 tokens_helper(_visitor, &#name.span)
227 },
228 VisitMut => quote! {
229 tokens_helper(_visitor, &mut #name.span)
230 },
231 }
232 }
233
David Tolnayf1e57f02019-05-08 15:30:39 -0700234 fn token_group_visit(kind: Kind, name: &Operand) -> TokenStream {
Carl Lerche058ff472019-02-13 16:23:52 -0800235 let name = name.tokens();
236 match kind {
Carl Lerche058ff472019-02-13 16:23:52 -0800237 Visit => quote! {
238 tokens_helper(_visitor, &#name.span)
239 },
240 VisitMut => quote! {
241 tokens_helper(_visitor, &mut #name.span)
242 },
243 }
244 }
245
David Tolnayf1e57f02019-05-08 15:30:39 -0700246 fn noop_visit(name: &Operand) -> TokenStream {
247 let name = name.tokens();
248 quote! {
249 skip!(#name)
Carl Lerche058ff472019-02-13 16:23:52 -0800250 }
251 }
252
253 fn visit(
254 ty: &types::Type,
255 features: &types::Features,
David Tolnay157c7eb2019-02-15 13:21:48 -0800256 defs: &types::Definitions,
Carl Lerche058ff472019-02-13 16:23:52 -0800257 kind: Kind,
258 name: &Operand,
259 ) -> Option<TokenStream> {
260 match ty {
David Tolnay157c7eb2019-02-15 13:21:48 -0800261 types::Type::Box(t) => box_visit(&*t, features, defs, kind, name),
262 types::Type::Vec(t) => vec_visit(&*t, features, defs, kind, name),
David Tolnay485973a2019-02-15 14:42:48 -0800263 types::Type::Punctuated(p) => punctuated_visit(&p.element, features, defs, kind, name),
David Tolnay157c7eb2019-02-15 13:21:48 -0800264 types::Type::Option(t) => option_visit(&*t, features, defs, kind, name),
265 types::Type::Tuple(t) => tuple_visit(t, features, defs, kind, name),
Carl Lerche058ff472019-02-13 16:23:52 -0800266 types::Type::Token(t) => {
David Tolnay157c7eb2019-02-15 13:21:48 -0800267 let repr = &defs.tokens[t];
268 let is_keyword = repr.chars().next().unwrap().is_alphabetic();
269 if is_keyword {
David Tolnayf1e57f02019-05-08 15:30:39 -0700270 Some(token_keyword_visit(kind, name))
Carl Lerche058ff472019-02-13 16:23:52 -0800271 } else {
David Tolnayf1e57f02019-05-08 15:30:39 -0700272 Some(token_punct_visit(kind, name))
Carl Lerche058ff472019-02-13 16:23:52 -0800273 }
274 }
David Tolnayf1e57f02019-05-08 15:30:39 -0700275 types::Type::Group(_) => Some(token_group_visit(kind, name)),
David Tolnayd3076572019-02-15 13:32:44 -0800276 types::Type::Syn(t) => {
Carl Lerche058ff472019-02-13 16:23:52 -0800277 fn requires_full(features: &types::Features) -> bool {
David Tolnay440fe582019-02-15 20:23:14 -0800278 features.any.contains("full") && features.any.len() == 1
Carl Lerche058ff472019-02-13 16:23:52 -0800279 }
280
David Tolnay397bd0b2019-02-15 20:51:10 -0800281 let res = simple_visit(t, kind, name);
Carl Lerche058ff472019-02-13 16:23:52 -0800282
David Tolnayc2be7b22019-02-15 18:48:31 -0800283 let target = defs.types.iter().find(|ty| ty.ident == *t).unwrap();
Carl Lerche058ff472019-02-13 16:23:52 -0800284
285 Some(
David Tolnayc2be7b22019-02-15 18:48:31 -0800286 if requires_full(&target.features) && !requires_full(features) {
Carl Lerche058ff472019-02-13 16:23:52 -0800287 quote! {
288 full!(#res)
289 }
290 } else {
291 res
292 },
293 )
294 }
Carl Lerchecbf7cc12019-02-15 14:09:31 -0800295 types::Type::Ext(t) if super::TERMINAL_TYPES.contains(&&t[..]) => {
296 Some(simple_visit(t, kind, name))
297 }
Carl Lerche058ff472019-02-13 16:23:52 -0800298 types::Type::Ext(_) | types::Type::Std(_) => None,
299 }
300 }
301
302 fn visit_features(features: &types::Features) -> TokenStream {
David Tolnay440fe582019-02-15 20:23:14 -0800303 let features = &features.any;
Carl Lerche058ff472019-02-13 16:23:52 -0800304 match features.len() {
305 0 => quote!(),
David Tolnay440fe582019-02-15 20:23:14 -0800306 1 => quote!(#[cfg(feature = #(#features)*)]),
307 _ => quote!(#[cfg(any(#(feature = #features),*))]),
Carl Lerche058ff472019-02-13 16:23:52 -0800308 }
309 }
310
David Tolnay157c7eb2019-02-15 13:21:48 -0800311 pub fn generate(state: &mut State, s: &types::Node, defs: &types::Definitions) {
David Tolnayc2be7b22019-02-15 18:48:31 -0800312 let features = visit_features(&s.features);
313 let under_name = under_name(&s.ident);
314 let ty = Ident::new(&s.ident, Span::call_site());
Carl Lerche058ff472019-02-13 16:23:52 -0800315 let visit_fn = Ident::new(&format!("visit_{}", under_name), Span::call_site());
316 let visit_mut_fn = Ident::new(&format!("visit_{}_mut", under_name), Span::call_site());
Carl Lerche058ff472019-02-13 16:23:52 -0800317
318 let mut visit_impl = TokenStream::new();
319 let mut visit_mut_impl = TokenStream::new();
Carl Lerche058ff472019-02-13 16:23:52 -0800320
David Tolnayc2be7b22019-02-15 18:48:31 -0800321 match &s.data {
322 types::Data::Enum(variants) => {
Carl Lerche058ff472019-02-13 16:23:52 -0800323 let mut visit_variants = TokenStream::new();
324 let mut visit_mut_variants = TokenStream::new();
Carl Lerche058ff472019-02-13 16:23:52 -0800325
David Tolnay75c5a172019-02-15 20:35:41 -0800326 for (variant, fields) in variants {
327 let variant_ident = Ident::new(variant, Span::call_site());
Carl Lerche058ff472019-02-13 16:23:52 -0800328
David Tolnay75c5a172019-02-15 20:35:41 -0800329 if fields.is_empty() {
Carl Lerche058ff472019-02-13 16:23:52 -0800330 visit_variants.append_all(quote! {
331 #ty::#variant_ident => {}
332 });
333 visit_mut_variants.append_all(quote! {
334 #ty::#variant_ident => {}
335 });
Carl Lerche058ff472019-02-13 16:23:52 -0800336 } else {
337 let mut bind_visit_fields = TokenStream::new();
338 let mut bind_visit_mut_fields = TokenStream::new();
Carl Lerche058ff472019-02-13 16:23:52 -0800339
340 let mut visit_fields = TokenStream::new();
341 let mut visit_mut_fields = TokenStream::new();
Carl Lerche058ff472019-02-13 16:23:52 -0800342
David Tolnay75c5a172019-02-15 20:35:41 -0800343 for (idx, ty) in fields.iter().enumerate() {
Carl Lerche058ff472019-02-13 16:23:52 -0800344 let name = format!("_binding_{}", idx);
345 let binding = Ident::new(&name, Span::call_site());
346
347 bind_visit_fields.append_all(quote! {
348 ref #binding,
349 });
350 bind_visit_mut_fields.append_all(quote! {
351 ref mut #binding,
352 });
Carl Lerche058ff472019-02-13 16:23:52 -0800353
354 let borrowed_binding = Borrowed(quote!(#binding));
Carl Lerche058ff472019-02-13 16:23:52 -0800355
356 visit_fields.append_all(
David Tolnayc2be7b22019-02-15 18:48:31 -0800357 visit(ty, &s.features, defs, Visit, &borrowed_binding)
David Tolnayf1e57f02019-05-08 15:30:39 -0700358 .unwrap_or_else(|| noop_visit(&borrowed_binding)),
Carl Lerche058ff472019-02-13 16:23:52 -0800359 );
360 visit_mut_fields.append_all(
David Tolnayc2be7b22019-02-15 18:48:31 -0800361 visit(ty, &s.features, defs, VisitMut, &borrowed_binding)
David Tolnayf1e57f02019-05-08 15:30:39 -0700362 .unwrap_or_else(|| noop_visit(&borrowed_binding)),
Carl Lerche058ff472019-02-13 16:23:52 -0800363 );
364
365 visit_fields.append_all(quote!(;));
366 visit_mut_fields.append_all(quote!(;));
Carl Lerche058ff472019-02-13 16:23:52 -0800367 }
368
369 visit_variants.append_all(quote! {
370 #ty::#variant_ident(#bind_visit_fields) => {
371 #visit_fields
372 }
373 });
374
375 visit_mut_variants.append_all(quote! {
376 #ty::#variant_ident(#bind_visit_mut_fields) => {
377 #visit_mut_fields
378 }
379 });
Carl Lerche058ff472019-02-13 16:23:52 -0800380 }
381 }
382
383 visit_impl.append_all(quote! {
384 match *_i {
385 #visit_variants
386 }
387 });
388
389 visit_mut_impl.append_all(quote! {
390 match *_i {
391 #visit_mut_variants
392 }
393 });
Carl Lerche058ff472019-02-13 16:23:52 -0800394 }
David Tolnayc2be7b22019-02-15 18:48:31 -0800395 types::Data::Struct(fields) => {
David Tolnayc2be7b22019-02-15 18:48:31 -0800396 for (field, ty) in fields {
David Tolnay485973a2019-02-15 14:42:48 -0800397 let id = Ident::new(&field, Span::call_site());
Carl Lerche058ff472019-02-13 16:23:52 -0800398 let ref_toks = Owned(quote!(_i.#id));
David Tolnayc2be7b22019-02-15 18:48:31 -0800399 let visit_field = visit(&ty, &s.features, defs, Visit, &ref_toks)
David Tolnayf1e57f02019-05-08 15:30:39 -0700400 .unwrap_or_else(|| noop_visit(&ref_toks));
Carl Lerche058ff472019-02-13 16:23:52 -0800401 visit_impl.append_all(quote! {
402 #visit_field;
403 });
David Tolnayc2be7b22019-02-15 18:48:31 -0800404 let visit_mut_field = visit(&ty, &s.features, defs, VisitMut, &ref_toks)
David Tolnayf1e57f02019-05-08 15:30:39 -0700405 .unwrap_or_else(|| noop_visit(&ref_toks));
Carl Lerche058ff472019-02-13 16:23:52 -0800406 visit_mut_impl.append_all(quote! {
407 #visit_mut_field;
408 });
Carl Lerche058ff472019-02-13 16:23:52 -0800409 }
410 }
David Tolnayf1e57f02019-05-08 15:30:39 -0700411 types::Data::Private => {}
Carl Lerche058ff472019-02-13 16:23:52 -0800412 }
413
414 state.visit_trait.append_all(quote! {
415 #features
416 fn #visit_fn(&mut self, i: &'ast #ty) {
417 #visit_fn(self, i)
418 }
419 });
420
421 state.visit_impl.append_all(quote! {
422 #features
423 pub fn #visit_fn<'ast, V: Visit<'ast> + ?Sized>(
424 _visitor: &mut V, _i: &'ast #ty
425 ) {
426 #visit_impl
427 }
428 });
429
430 state.visit_mut_trait.append_all(quote! {
431 #features
432 fn #visit_mut_fn(&mut self, i: &mut #ty) {
433 #visit_mut_fn(self, i)
434 }
435 });
436
437 state.visit_mut_impl.append_all(quote! {
438 #features
439 pub fn #visit_mut_fn<V: VisitMut + ?Sized>(
440 _visitor: &mut V, _i: &mut #ty
441 ) {
442 #visit_mut_impl
443 }
444 });
Carl Lerche058ff472019-02-13 16:23:52 -0800445 }
446}
447
Carl Lerchecbf7cc12019-02-15 14:09:31 -0800448const TERMINAL_TYPES: &[&str] = &["Span", "Ident"];
449
David Tolnayf9bb8ff2019-02-15 13:10:14 -0800450pub fn generate(defs: &types::Definitions) {
David Tolnay4bc55232019-02-15 21:09:00 -0800451 let mut state = codegen::State::default();
452 for s in &defs.types {
453 codegen::generate(&mut state, s, defs);
454 }
455 for tt in TERMINAL_TYPES {
456 let s = types::Node {
David Tolnayc2be7b22019-02-15 18:48:31 -0800457 ident: tt.to_string(),
458 features: types::Features::default(),
459 data: types::Data::Private,
David Tolnay4bc55232019-02-15 21:09:00 -0800460 };
461 codegen::generate(&mut state, &s, defs);
Carl Lerche058ff472019-02-13 16:23:52 -0800462 }
463
464 let full_macro = quote! {
465 #[cfg(feature = "full")]
466 macro_rules! full {
467 ($e:expr) => {
468 $e
469 };
470 }
471
472 #[cfg(all(feature = "derive", not(feature = "full")))]
473 macro_rules! full {
474 ($e:expr) => {
475 unreachable!()
476 };
477 }
478 };
479
480 let skip_macro = quote! {
481 #[cfg(any(feature = "full", feature = "derive"))]
482 macro_rules! skip {
483 ($($tt:tt)*) => {};
484 }
485 };
486
Carl Lerche058ff472019-02-13 16:23:52 -0800487 let visit_trait = state.visit_trait;
488 let visit_impl = state.visit_impl;
David Tolnaycec98a72019-05-08 15:23:19 -0700489 file::write(
Carl Lerche058ff472019-02-13 16:23:52 -0800490 VISIT_SRC,
491 quote! {
492 #![cfg_attr(feature = "cargo-clippy", allow(trivially_copy_pass_by_ref))]
493
494 use *;
495 #[cfg(any(feature = "full", feature = "derive"))]
496 use punctuated::Punctuated;
497 use proc_macro2::Span;
498 #[cfg(any(feature = "full", feature = "derive"))]
499 use gen::helper::visit::*;
500
501 #full_macro
502 #skip_macro
503
504 /// Syntax tree traversal to walk a shared borrow of a syntax tree.
505 ///
506 /// See the [module documentation] for details.
507 ///
508 /// [module documentation]: index.html
509 ///
510 /// *This trait is available if Syn is built with the `"visit"` feature.*
511 pub trait Visit<'ast> {
512 #visit_trait
513 }
514
515 #visit_impl
516 },
517 );
518
519 let visit_mut_trait = state.visit_mut_trait;
520 let visit_mut_impl = state.visit_mut_impl;
David Tolnaycec98a72019-05-08 15:23:19 -0700521 file::write(
Carl Lerche058ff472019-02-13 16:23:52 -0800522 VISIT_MUT_SRC,
523 quote! {
524 use *;
525 #[cfg(any(feature = "full", feature = "derive"))]
526 use punctuated::Punctuated;
527 use proc_macro2::Span;
528 #[cfg(any(feature = "full", feature = "derive"))]
529 use gen::helper::visit_mut::*;
530
531 #full_macro
532 #skip_macro
533
534 /// Syntax tree traversal to mutate an exclusive borrow of a syntax tree in
535 /// place.
536 ///
537 /// See the [module documentation] for details.
538 ///
539 /// [module documentation]: index.html
540 ///
541 /// *This trait is available if Syn is built with the `"visit-mut"` feature.*
542 pub trait VisitMut {
543 #visit_mut_trait
544 }
545
546 #visit_mut_impl
547 },
548 );
549}