Skip to main content

template_quote_impl/
lib.rs

1extern crate proc_macro;
2extern crate proc_macro2;
3extern crate syn;
4
5use proc_macro::TokenStream;
6use proc_macro2::TokenStream as TokenStream2;
7use proc_macro2::{Delimiter, Group, Literal, Punct, Spacing, Span, TokenTree};
8use proc_macro_error::{abort, proc_macro_error};
9use quote::{quote as qquote, quote_spanned as qquote_spanned, TokenStreamExt};
10use std::collections::{HashSet, VecDeque};
11use syn::{parse_quote, Expr, Ident, Path, Token};
12
13/// The parser for `quote!` macro.
14///
15/// this struct has some global configs to be initialized using
16/// `Default::default()` or `syn::parse::Parse::parse()`. User can specify
17/// configs using `quote_configured!` macro.
18struct ParseEnvironment {
19	span: Expr,
20	path_proc_macro2: Path,
21	path_quote: Path,
22	path_core: Path,
23	id_stream: Ident,
24	id_counter: Ident,
25}
26
27impl syn::parse::Parse for ParseEnvironment {
28	fn parse(input: syn::parse::ParseStream<'_>) -> syn::parse::Result<Self> {
29		// Read comma-separated `key = value` fields
30		let mut fields: Vec<(Ident, Expr)> = Vec::new();
31		while !input.is_empty() {
32			let ident = input.parse()?;
33			input.parse::<Token![:]>()?;
34			let expr = input.parse()?;
35			fields.push((ident, expr));
36			if !input.is_empty() {
37				input.parse::<Token![,]>()?;
38			}
39		}
40		let mut this: ParseEnvironment = Default::default();
41		for (id, expr) in fields {
42			match (id.to_string().as_str(), expr) {
43				("proc_macro2", Expr::Path(pat)) => {
44					this.path_proc_macro2 = pat.path;
45				}
46				("quote", Expr::Path(pat)) => {
47					this.path_quote = pat.path;
48				}
49				("core", Expr::Path(pat)) => {
50					this.path_core = pat.path;
51				}
52				("span", expr) => {
53					this.span = expr;
54				}
55				(key, _) => {
56					return Err(input.error(format!("Bad config name: {}", key)));
57				}
58			}
59		}
60		Ok(this)
61	}
62}
63
64impl core::default::Default for ParseEnvironment {
65	fn default() -> Self {
66		Self {
67			span: parse_quote! { ::proc_macro2::Span::call_site() },
68			path_proc_macro2: parse_quote! { ::proc_macro2 },
69			path_quote: parse_quote! { ::template_quote },
70			path_core: parse_quote! { ::core },
71			id_stream: Ident::new("__template_quote_stream", Span::mixed_site()),
72			id_counter: Ident::new("__template_quote_idcnt", Span::mixed_site()),
73		}
74	}
75}
76
77fn eat_terminator(input: &mut VecDeque<TokenTree>) -> bool {
78	// Recognize a `..` rest pattern (two joint `.` puncts) wherever it appears
79	// in a tuple/slice/struct pattern, not only when it is the final element.
80	match (input.pop_front(), input.pop_front()) {
81		(Some(TokenTree::Punct(p1)), Some(TokenTree::Punct(p2)))
82			if p1.as_char() == '.' && p2.as_char() == '.' && p1.spacing() == Spacing::Joint =>
83		{
84			true
85		}
86		(a, b) => {
87			if let Some(b) = b {
88				input.push_front(b);
89			}
90			if let Some(a) = a {
91				input.push_front(a);
92			}
93			false
94		}
95	}
96}
97fn eat_comma(input: &mut VecDeque<TokenTree>) -> bool {
98	match input.pop_front() {
99		Some(TokenTree::Punct(p)) if p.as_char() == ',' && p.spacing() == Spacing::Alone => true,
100		Some(tt) => {
101			input.push_front(tt);
102			false
103		}
104		_ => false,
105	}
106}
107fn collect_ident(input: &mut VecDeque<TokenTree>) -> Result<Vec<Ident>, ()> {
108	fn collect_paren_stream(
109		mut input: VecDeque<TokenTree>,
110		ret: &mut Vec<Ident>,
111	) -> Result<(), ()> {
112		while !input.is_empty() {
113			// A `..` rest pattern binds nothing; consume a following separator
114			// so remaining elements (e.g. `[a, .., b]`) are still parsed.
115			if eat_terminator(&mut input) {
116				if !input.is_empty() && !eat_comma(&mut input) {
117					return Err(());
118				}
119				continue;
120			}
121			ret.extend(collect_ident(&mut input)?);
122			if !input.is_empty() && !eat_comma(&mut input) {
123				return Err(());
124			}
125		}
126		Ok(())
127	}
128	let mut ret = Vec::new();
129	// Skip an optional leading `::` (absolute path in e.g. `::core::..::Some(x)`).
130	if matches!(
131		(input.front(), input.get(1)),
132		(Some(TokenTree::Punct(p1)), Some(TokenTree::Punct(p2)))
133			if p1.as_char() == ':' && p2.as_char() == ':' && p1.spacing() == Spacing::Joint
134	) {
135		input.pop_front();
136		input.pop_front();
137	}
138	// Skip binding modifiers: `ref`, `mut`, `ref mut`.
139	while matches!(input.front(), Some(TokenTree::Ident(id)) if id == "ref" || id == "mut") {
140		input.pop_front();
141	}
142	match input.pop_front() {
143		// Parse `let (..) = ..`
144		Some(TokenTree::Group(g))
145			if (g.delimiter() == Delimiter::Parenthesis || g.delimiter() == Delimiter::Bracket) =>
146		{
147			collect_paren_stream(g.stream().into_iter().collect(), &mut ret)?;
148			Ok(ret)
149		}
150		Some(TokenTree::Ident(id)) => {
151			loop {
152				match input.pop_front() {
153					Some(TokenTree::Punct(colon))
154						if colon.as_char() == ':' && colon.spacing() == Spacing::Joint =>
155					{
156						if matches!(
157						input.pop_front(),
158						Some(TokenTree::Punct(colon)) if colon.as_char() == ':' && colon.spacing() == Spacing::Alone
159						) && matches!(input.pop_front(), Some(TokenTree::Ident(_)))
160						{
161							continue;
162						} else {
163							return Err(());
164						}
165					}
166					Some(o) => input.push_front(o),
167					None => (),
168				}
169				break;
170			}
171			match input.pop_front() {
172				// Parse `let Ident ( .. ) = ..`
173				Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Parenthesis => {
174					collect_paren_stream(g.stream().into_iter().collect(), &mut ret)?;
175					Ok(ret)
176				}
177				// Parse `let Ident { key: value, value2, ... } = ..`
178				Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Brace => {
179					let mut inner: VecDeque<TokenTree> = g.stream().into_iter().collect();
180					while !inner.is_empty() && !eat_terminator(&mut inner) {
181						// Skip `ref`/`mut` modifiers on shorthand fields.
182						while matches!(inner.front(), Some(TokenTree::Ident(id)) if id == "ref" || id == "mut")
183						{
184							inner.pop_front();
185						}
186						match inner.pop_front() {
187							Some(TokenTree::Ident(key)) => {
188								// `key: <pattern>` (recurse into the sub-pattern, so
189								// non-ident values like `a: (x, y)` work) or the
190								// field-shorthand `key`.
191								if matches!(inner.front(), Some(TokenTree::Punct(colon)) if colon.as_char() == ':')
192								{
193									inner.pop_front(); // the field separator `:`
194									ret.extend(collect_ident(&mut inner)?);
195								} else {
196									ret.push(key);
197								}
198							}
199							_ => return Err(()),
200						}
201						if !inner.is_empty() && !eat_comma(&mut inner) {
202							return Err(());
203						}
204					}
205					Ok(ret)
206				}
207				// Parse `let ident = ..`
208				Some(o) => {
209					input.push_front(o);
210					ret.push(id);
211					Ok(ret)
212				}
213				None => {
214					ret.push(id);
215					Ok(ret)
216				}
217			}
218		}
219		_ => Err(()),
220	}
221}
222
223fn collect_ident_eq(input: &mut VecDeque<TokenTree>) -> Result<Vec<Ident>, ()> {
224	let v = collect_ident(input)?;
225	match input.pop_front() {
226		Some(TokenTree::Punct(eq)) if eq.as_char() == '=' && eq.spacing() == Spacing::Alone => {
227			Ok(v)
228		}
229		_ => Err(()),
230	}
231}
232
233fn collect_punct_in_expr(mut stream: VecDeque<TokenTree>) -> (Vec<Ident>, VecDeque<TokenTree>) {
234	let mut output = VecDeque::new();
235	let mut ids = Vec::new();
236	while let Some(tt) = stream.pop_front() {
237		match tt {
238			TokenTree::Punct(punct) if punct.as_char() == '#' => match stream.pop_front() {
239				Some(TokenTree::Ident(id)) => {
240					ids.push(id.clone());
241					output.push_back(TokenTree::Ident(id));
242				}
243				Some(o) => {
244					output.push_back(TokenTree::Punct(punct));
245					output.push_back(o);
246				}
247				None => {
248					output.push_back(TokenTree::Punct(punct));
249				}
250			},
251			// Recurse into groups so that `#var` nested inside a group (e.g.
252			// `(#v).iter()` or `matches!(#v, _)`) is interpolated just like at
253			// the top level, instead of being left as a literal `# v`.
254			TokenTree::Group(group) => {
255				let (inner_ids, inner) =
256					collect_punct_in_expr(group.stream().into_iter().collect());
257				ids.extend(inner_ids);
258				let mut new_group = Group::new(group.delimiter(), inner.into_iter().collect());
259				new_group.set_span(group.span());
260				output.push_back(TokenTree::Group(new_group));
261			}
262			o => output.push_back(o),
263		}
264	}
265	(ids, output)
266}
267
268fn hash_will_be_processed(next: Option<&TokenTree>, rest: &VecDeque<TokenTree>) -> bool {
269	match next {
270		Some(TokenTree::Ident(_)) => true,
271		Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::Brace => true,
272		Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::Parenthesis => {
273			match (rest.front(), rest.get(1)) {
274				(Some(TokenTree::Group(group2)), _) if group2.delimiter() == Delimiter::Brace => {
275					true
276				}
277				(Some(TokenTree::Punct(punct)), _) if punct.as_char() == '*' => true,
278				(Some(TokenTree::Punct(_)), Some(TokenTree::Punct(punct2)))
279					if punct2.as_char() == '*' =>
280				{
281					true
282				}
283				(Some(TokenTree::Punct(_)), Some(TokenTree::Group(group2)))
284					if group2.delimiter() == Delimiter::Brace =>
285				{
286					true
287				}
288				_ => false,
289			}
290		}
291		_ => false,
292	}
293}
294
295impl ParseEnvironment {
296	fn emit_ident(&self, ident: &Ident) -> TokenStream2 {
297		let Self {
298			span,
299			path_proc_macro2,
300			path_quote,
301			id_stream,
302			..
303		} = self;
304		let s = ident.to_string();
305		if let Some(s) = s.strip_prefix("r#") {
306			qquote! {
307				<_ as #path_quote::ToTokens>::to_tokens(&#path_proc_macro2::Ident::new_raw(#s, (#span)), &mut #id_stream);
308			}
309		} else {
310			qquote! {
311				<_ as #path_quote::ToTokens>::to_tokens(&#path_proc_macro2::Ident::new(#s, (#span)), &mut #id_stream);
312			}
313		}
314	}
315
316	fn emit_literal(&self, lit: &Literal) -> TokenStream2 {
317		let Self {
318			span,
319			path_proc_macro2,
320			id_stream,
321			..
322		} = self;
323		let s = lit.to_string();
324		qquote! {
325			{
326				let ts: #path_proc_macro2::TokenStream = #s.parse().expect("Invalid literal str");
327				#id_stream.extend(ts.into_iter().map(|mut t| {
328					t.set_span(#span);
329					t
330				}));
331			}
332		}
333	}
334
335	fn emit_punct(&self, punct: &Punct) -> TokenStream2 {
336		let Self {
337			span,
338			path_proc_macro2,
339			path_quote,
340			id_stream,
341			..
342		} = self;
343		let p = punct.as_char();
344		let spacing = match punct.spacing() {
345			Spacing::Alone => qquote! {#path_proc_macro2::Spacing::Alone},
346			Spacing::Joint => qquote! {#path_proc_macro2::Spacing::Joint},
347		};
348		qquote! {
349			<_ as #path_quote::ToTokens>::to_tokens(&{
350				let mut p = #path_proc_macro2::Punct::new(#p, #spacing);
351				p.set_span(#span);
352				p
353			}, &mut #id_stream);
354		}
355	}
356
357	fn emit_punct_with_spacing(&self, punct: &Punct, spacing: Spacing) -> TokenStream2 {
358		let mut p = Punct::new(punct.as_char(), spacing);
359		p.set_span(punct.span());
360		self.emit_punct(&p)
361	}
362
363	fn emit_group(&self, delim: &Delimiter, inner: TokenStream2) -> TokenStream2 {
364		let Self {
365			span,
366			path_proc_macro2,
367			path_core,
368			id_stream,
369			..
370		} = self;
371		let delim = match delim {
372			Delimiter::Parenthesis => qquote! { #path_proc_macro2::Delimiter::Parenthesis },
373			Delimiter::Brace => qquote! { #path_proc_macro2::Delimiter::Brace },
374			Delimiter::Bracket => qquote! { #path_proc_macro2::Delimiter::Bracket },
375			Delimiter::None => qquote! { #path_proc_macro2::Delimiter::None },
376		};
377		qquote! {
378			#id_stream.extend(
379				#path_core::option::Option::Some(
380					#path_proc_macro2::TokenTree::Group(
381						{
382							let mut g = #path_proc_macro2::Group::new(#delim, {
383								let mut #id_stream = #path_proc_macro2::TokenStream::new();
384								{ #inner }
385								#id_stream
386							});
387							g.set_span(#span);
388							g
389						}
390					)
391				)
392			);
393		}
394	}
395
396	fn parse_conditional(
397		&self,
398		conditional: TokenStream2,
399		input: VecDeque<TokenTree>,
400		vals: &mut HashSet<Ident>,
401		sep: Option<Punct>,
402		inline_expr_dict: &mut Vec<(Ident, TokenStream2, Span)>,
403	) -> TokenStream2 {
404		fn parse_if_inner(
405			mut cond: VecDeque<TokenTree>,
406		) -> (Vec<Ident>, Vec<Ident>, Vec<TokenTree>) {
407			let span_ts: TokenStream2 = cond.iter().cloned().collect();
408			let mut bak: Vec<_> = cond.iter().cloned().collect();
409			match cond.pop_front() {
410				Some(TokenTree::Ident(id_let)) if id_let == "let" => {
411					let removing_ids = collect_ident_eq(&mut cond).unwrap_or_else(|_| {
412						abort!(span_ts, "malformed `if let` conditional in template")
413					});
414					bak.truncate(bak.len() - cond.len());
415					let (appending_ids, rem) = collect_punct_in_expr(cond);
416					bak.extend(rem);
417					(removing_ids, appending_ids, bak)
418				}
419				Some(o) => {
420					cond.push_front(o);
421					bak.truncate(bak.len() - cond.len());
422					let (appending_ids, rem) = collect_punct_in_expr(cond);
423					bak.extend(rem);
424					(vec![], appending_ids, bak)
425				}
426				None => abort!(span_ts, "empty `if` conditional in template"),
427			}
428		}
429		let mut cond: VecDeque<TokenTree> = conditional.clone().into_iter().collect();
430		let cond_len = cond.len();
431		let (removing_ids, appending_ids, cond) = match (cond.pop_front(), sep.is_some()) {
432			(Some(TokenTree::Ident(id)), false) if id == "if" => {
433				let (removing_ids, appending_ids, rem) = parse_if_inner(cond);
434				(
435					removing_ids,
436					appending_ids,
437					Some(TokenTree::Ident(id)).into_iter().chain(rem).collect(),
438				)
439			}
440			(Some(TokenTree::Ident(id)), false) if id == "else" => {
441				match cond.pop_front() {
442					// else if
443					Some(TokenTree::Ident(id_if)) if id_if == "if" => {
444						let (removing_ids, appending_ids, rem) = parse_if_inner(cond);
445						(
446							removing_ids,
447							appending_ids,
448							vec![TokenTree::Ident(id), TokenTree::Ident(id_if)]
449								.into_iter()
450								.chain(rem)
451								.collect(),
452						)
453					}
454					Some(_) => {
455						abort!(
456							conditional.clone(),
457							"malformed `else` in template (expected `else` or `else if`)"
458						)
459					}
460					None => (vec![], vec![], conditional),
461				}
462			}
463			(Some(TokenTree::Ident(id)), false) if id == "let" => {
464				let removing_ids = collect_ident_eq(&mut cond).unwrap_or_else(|_| {
465					abort!(conditional.clone(), "malformed `let` binding in template")
466				});
467				let n = cond_len - cond.len();
468				let (appending_ids, rem) = collect_punct_in_expr(cond);
469				let conditional: TokenStream2 =
470					conditional.clone().into_iter().take(n).chain(rem).collect();
471				(removing_ids, appending_ids, qquote! {#conditional ;})
472			}
473			(Some(TokenTree::Ident(id)), _) if id == "while" => match cond.pop_front() {
474				Some(TokenTree::Ident(id_let)) if id_let == "let" => {
475					let removing_ids = collect_ident_eq(&mut cond).unwrap_or_else(|_| {
476						abort!(
477							conditional.clone(),
478							"malformed `while let` loop in template"
479						)
480					});
481					let n = cond_len - cond.len();
482					let (appending_ids, rem) = collect_punct_in_expr(cond);
483					let conditional: TokenStream2 =
484						conditional.clone().into_iter().take(n).chain(rem).collect();
485					(removing_ids, appending_ids, conditional)
486				}
487				Some(o) => {
488					cond.push_front(o);
489					let n = cond_len - cond.len();
490					let (appending_ids, rem) = collect_punct_in_expr(cond);
491					let conditional: TokenStream2 =
492						conditional.clone().into_iter().take(n).chain(rem).collect();
493					(vec![], appending_ids, conditional)
494				}
495				None => abort!(conditional.clone(), "empty `while` conditional in template"),
496			},
497			(Some(TokenTree::Ident(id)), _) if id == "for" => {
498				match (collect_ident(&mut cond), cond.pop_front()) {
499					(Ok(v), Some(TokenTree::Ident(id_in))) if id_in == "in" => {
500						let n = cond_len - cond.len();
501						let (appending_ids, rem) = collect_punct_in_expr(cond);
502						let conditional: TokenStream2 =
503							conditional.clone().into_iter().take(n).chain(rem).collect();
504						(v, appending_ids, conditional)
505					}
506					_ => abort!(
507						conditional.clone(),
508						"malformed `for` loop in template (expected `for <pat> in <expr>`)"
509					),
510				}
511			}
512			_ => abort!(
513				conditional.clone(),
514				"malformed conditional in template (expected `if`, `else`, `let`, `while` or `for`)"
515			),
516		};
517		let inner = self.parse_inner(input, vals, inline_expr_dict);
518		for id in removing_ids {
519			vals.remove(&id);
520		}
521		for id in appending_ids {
522			vals.insert(id);
523		}
524		if let Some(sep) = sep {
525			let code_sep = self.emit_punct(&sep);
526			let id_counter = &self.id_counter;
527			qquote! {
528				{
529					let mut #id_counter = false;
530					#cond {
531						if #id_counter { #code_sep }
532						#id_counter = true;
533						#inner
534					}
535				}
536			}
537		} else {
538			qquote! {
539				#cond {
540					#inner
541				}
542			}
543		}
544	}
545
546	fn parse_iteration(
547		&self,
548		input: VecDeque<TokenTree>,
549		vals: &mut HashSet<Ident>,
550		sep: Option<Punct>,
551		inline_expr_dict: &mut Vec<(Ident, TokenStream2, Span)>,
552	) -> TokenStream2 {
553		let Self {
554			path_quote,
555			path_core,
556			..
557		} = self;
558		let debug_str = input
559			.iter()
560			.take(6)
561			.map(|item| match item {
562				TokenTree::Group(g) => match g.delimiter() {
563					Delimiter::Parenthesis => "( .. )".to_owned(),
564					Delimiter::Brace => "{ .. }".to_owned(),
565					Delimiter::Bracket => "[ .. ]".to_owned(),
566					Delimiter::None => "..".to_owned(),
567				},
568				_ => format!("{}", item),
569			})
570			.chain(if input.len() > 6 {
571				Some("..".to_owned())
572			} else {
573				None
574			})
575			.collect::<Vec<_>>()
576			.join(" ");
577		// Snapshot the repetition-group tokens before `input` is consumed below,
578		// so a diagnostic can point directly at the offending group.
579		let group_tokens: TokenStream2 = input.iter().cloned().collect();
580		let mut inner_vals = HashSet::new();
581		let inner_output = self.parse_inner(input, &mut inner_vals, inline_expr_dict);
582		let code_sep = sep.map(|sep| self.emit_punct(&Punct::new(sep.as_char(), Spacing::Alone)));
583		let val_nam = code_sep
584			.as_ref()
585			.map(|_| self.id_counter.clone())
586			.into_iter()
587			.collect::<Vec<_>>();
588		vals.extend(inner_vals.iter().cloned());
589		let vars: Vec<Ident> = inner_vals.iter().cloned().collect();
590		if vars.is_empty() {
591			abort!(
592				group_tokens,
593				"Repetition group `#( {} )*` contains no interpolation variable (`#var`)", debug_str;
594				note = "a repetition needs at least one `#var` inside it to drive the iteration"
595			);
596		}
597		// Fresh call-site helper idents per distinct interpolation var: the bound
598		// iterator and its runtime "is iterable" flag.
599		let it_idents: Vec<Ident> = (0..vars.len())
600			.map(|i| Ident::new(&format!("__template_quote_it_{}", i), Span::mixed_site()))
601			.collect();
602		let m_idents: Vec<Ident> = (0..vars.len())
603			.map(|i| Ident::new(&format!("__template_quote_m_{}", i), Span::mixed_site()))
604			.collect();
605		let bindings = vars
606			.iter()
607			.zip(&it_idents)
608			.zip(&m_idents)
609			.map(|((var, it), m)| {
610				qquote! {
611					#[allow(unused_mut)]
612					let (mut #it, #m) = (#var).__template_quote_into_iter();
613				}
614			});
615		// Panic at runtime (preserving historical behavior) if no var is iterable.
616		let checker = m_idents
617			.iter()
618			.fold(qquote! {false}, |acc, m| qquote! {#acc || #m});
619		// Left-nested tuple pattern of the user idents matching the left-nested
620		// `zip` chain of the bound iterators.
621		let (first_var, rest_vars) = vars.split_first().unwrap();
622		let (first_it, rest_it) = it_idents.split_first().unwrap();
623		let idents_in_tuple = rest_vars
624			.iter()
625			.fold(qquote! {#first_var}, |prev, next| qquote! {(#prev, #next)});
626		let zipped = rest_it
627			.iter()
628			.fold(qquote! {#first_it}, |prev, next| qquote! {#prev.zip(#next)});
629		qquote! {
630			{
631				#(let mut #val_nam = false;)*
632				#[allow(unused_imports)]
633				use #path_quote::__private::ext::{
634					RepAsIteratorExt as _, RepIteratorExt as _, RepToTokensExt as _,
635				};
636				#(#bindings)*
637				if !(#checker) {
638					#path_core::panic!("Cannot iterate the group: #( {} )*", #debug_str);
639				}
640				for #idents_in_tuple in #zipped {
641					#(
642						if #val_nam { #code_sep }
643						#val_nam = true;
644					)*
645					#inner_output
646				}
647			}
648		}
649	}
650
651	fn parse(&self, input: TokenStream2) -> TokenStream2 {
652		let Self {
653			path_proc_macro2,
654			id_stream,
655			..
656		} = self;
657		let mut hs = HashSet::new();
658		let mut dict = Vec::new();
659		let result = self.parse_inner(input.into_iter().collect(), &mut hs, &mut dict);
660		let inline_vals_code =
661			dict.into_iter()
662				.fold(TokenStream2::new(), |acc, (id, inner, span)| {
663					qquote_spanned! { span =>
664						#acc
665						let #id = { #inner };
666					}
667				});
668		qquote! {
669			{
670				let mut #id_stream= #path_proc_macro2::TokenStream::new();
671				#inline_vals_code
672				{ #result }
673				#id_stream
674			}
675		}
676	}
677
678	fn parse_inner(
679		&self,
680		mut input: VecDeque<TokenTree>,
681		vals: &mut HashSet<Ident>,
682		inline_expr_dict: &mut Vec<(Ident, TokenStream2, Span)>,
683	) -> TokenStream2 {
684		let Self {
685			path_quote,
686			id_stream,
687			..
688		} = self;
689		let mut output = TokenStream2::new();
690		while let Some(token) = input.pop_front() {
691			match token {
692				TokenTree::Group(group) => {
693					let inner = group.stream().into_iter().collect();
694					let result = self.parse_inner(inner, vals, inline_expr_dict);
695					let result = self.emit_group(&group.delimiter(), result);
696					output.append_all(result);
697				}
698				TokenTree::Punct(punct) => match (punct.as_char(), input.pop_front()) {
699					// # val
700					('#', Some(TokenTree::Ident(ident))) => {
701						vals.insert(ident.clone());
702						output.append_all(
703							qquote! { <_ as #path_quote::ToTokens>::to_tokens(&#ident, &mut #id_stream); },
704						);
705					}
706					// # { ... }
707					('#', Some(TokenTree::Group(group)))
708						if group.delimiter() == Delimiter::Brace =>
709					{
710						let inner = group.stream().into_iter().collect::<VecDeque<_>>();
711						if inner.is_empty() {
712							// `#{}` expands to nothing.
713						} else {
714							// The inner block is a statement block when it ends with
715							// `;`, otherwise it is an expression to interpolate.
716							let is_expr = !matches!(
717								inner.back(),
718								Some(TokenTree::Punct(p)) if p.as_char() == ';'
719							);
720							let (appending_ids, inner) = collect_punct_in_expr(inner);
721							for id in appending_ids {
722								vals.insert(id);
723							}
724							let stream: TokenStream2 = inner.into_iter().collect();
725							if is_expr {
726								output.append_all(qquote! {
727									<_ as #path_quote::ToTokens>::to_tokens(&{
728										#stream
729									}, &mut #id_stream);
730								});
731							} else {
732								output.append_all(qquote! {
733									{ #stream }
734								});
735							}
736						}
737					}
738					('#', Some(TokenTree::Group(group)))
739						if group.delimiter() == Delimiter::Parenthesis =>
740					{
741						match input.pop_front() {
742							// # ( ... ) { ... }
743							Some(TokenTree::Group(group2))
744								if group2.delimiter() == Delimiter::Brace =>
745							{
746								output.append_all(self.parse_conditional(
747									group.stream(),
748									group2.stream().into_iter().collect(),
749									vals,
750									None,
751									inline_expr_dict,
752								));
753							}
754							// # ( ... ) *
755							Some(TokenTree::Punct(punct)) if punct.as_char() == '*' => output
756								.append_all(self.parse_iteration(
757									group.stream().into_iter().collect(),
758									vals,
759									None,
760									inline_expr_dict,
761								)),
762							Some(TokenTree::Punct(punct0)) => match input.pop_front() {
763								// # ( ... ) [SEP] *
764								Some(TokenTree::Punct(punct1)) if punct1.as_char() == '*' => output
765									.append_all(self.parse_iteration(
766										group.stream().into_iter().collect(),
767										vals,
768										Some(punct0),
769										inline_expr_dict,
770									)),
771								// # ( ... ) [SEP] { ... }
772								Some(TokenTree::Group(group2))
773									if group2.delimiter() == Delimiter::Brace =>
774								{
775									output.append_all(self.parse_conditional(
776										group.stream(),
777										group2.stream().into_iter().collect(),
778										vals,
779										Some(punct0),
780										inline_expr_dict,
781									));
782								}
783								o => {
784									if let Some(o) = o {
785										input.push_front(o);
786									}
787									input.push_front(TokenTree::Punct(punct0));
788									input.push_front(TokenTree::Group(group));
789									output.append_all(self.emit_punct(&punct));
790								}
791							},
792							o => {
793								if let Some(o) = o {
794									input.push_front(o)
795								}
796								input.push_front(TokenTree::Group(group));
797								output.append_all(self.emit_punct(&punct));
798							}
799						}
800					}
801					(_, o) => {
802						// A punct written `Joint` immediately before an interpolation
803						// that will be processed must be downgraded to `Alone`: the
804						// macro cannot know what the interpolation expands to, and a
805						// `Joint` punct would silently fuse with a leading punct in
806						// the expansion (e.g. `-` + `>` -> `->`). This matches the
807						// spacing behaviour of the upstream `quote!` macro.
808						let adjust_joint_before_hash = punct.spacing() == Spacing::Joint
809							&& matches!(o, Some(TokenTree::Punct(ref p)) if p.as_char() == '#')
810							&& hash_will_be_processed(input.front(), &input);
811						if let Some(o) = o {
812							input.push_front(o);
813						}
814						if adjust_joint_before_hash {
815							output.append_all(self.emit_punct_with_spacing(&punct, Spacing::Alone));
816						} else {
817							output.append_all(self.emit_punct(&punct));
818						}
819					}
820				},
821				TokenTree::Ident(o) => output.append_all(self.emit_ident(&o)),
822				TokenTree::Literal(o) => output.append_all(self.emit_literal(&o)),
823			}
824		}
825		output
826	}
827}
828
829#[proc_macro]
830#[proc_macro_error]
831pub fn quote(input: TokenStream) -> TokenStream {
832	let env: ParseEnvironment = Default::default();
833	env.parse(input.into()).into()
834}
835
836#[proc_macro]
837#[proc_macro_error]
838pub fn quote_configured(input: TokenStream) -> TokenStream {
839	let input0: TokenStream2 = input.into();
840	let mut input = VecDeque::new();
841	input.extend(input0.into_iter());
842	let env: ParseEnvironment = match input.pop_front() {
843		Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Brace => syn::parse2(g.stream())
844			.unwrap_or_else(|e| abort!(g, "malformed `quote_configured!` config block: {}", e)),
845		Some(other) => abort!(
846			other,
847			"`quote_configured!` expects a `{{ key: path, ... }}` config block first"
848		),
849		None => abort!(
850			Span::call_site(),
851			"`quote_configured!` expects a `{{ key: path, ... }} => ...` invocation"
852		),
853	};
854	match input.pop_front() {
855		Some(TokenTree::Punct(p)) if p.as_char() == '=' && p.spacing() == Spacing::Joint => (),
856		Some(other) => abort!(
857			other,
858			"`quote_configured!` expects `=>` after the config block"
859		),
860		None => abort!(
861			Span::call_site(),
862			"`quote_configured!` expects `=>` after the config block"
863		),
864	}
865	match input.pop_front() {
866		Some(TokenTree::Punct(p)) if p.as_char() == '>' && p.spacing() == Spacing::Alone => (),
867		Some(other) => abort!(
868			other,
869			"`quote_configured!` expects `=>` after the config block"
870		),
871		None => abort!(
872			Span::call_site(),
873			"`quote_configured!` expects `=>` after the config block"
874		),
875	}
876	let mut stream = TokenStream2::new();
877	stream.extend(input);
878	env.parse(stream).into()
879}
880
881#[proc_macro]
882#[proc_macro_error]
883pub fn quote_spanned(input: TokenStream) -> TokenStream {
884	let input0: TokenStream2 = input.into();
885	let mut input = VecDeque::new();
886	input.extend(input0.into_iter());
887	let mut span = TokenStream2::new(); // Tokens before '=>'
888	loop {
889		match input.pop_front() {
890			Some(TokenTree::Punct(p)) if p.as_char() == '=' && p.spacing() == Spacing::Joint => {
891				match input.pop_front() {
892					Some(TokenTree::Punct(p))
893						if p.as_char() == '>' && p.spacing() == Spacing::Alone =>
894					{
895						// Found '=>'.
896						break;
897					}
898					Some(o) => {
899						span.extend(Some(TokenTree::Punct(p)));
900						input.push_front(o);
901					}
902					None => {
903						span.extend(Some(TokenTree::Punct(p)));
904					}
905				}
906			}
907			Some(o) => span.extend(Some(o)),
908			None => abort!(
909				Span::call_site(),
910				"`quote_spanned!` expects `<span-expr> => <tokens>` (no `=>` found)"
911			),
912		}
913	}
914	let mut env: ParseEnvironment = Default::default();
915	let span_tokens = span.clone();
916	env.span = syn::parse2(span).unwrap_or_else(|e| {
917		abort!(
918			span_tokens,
919			"the `<span>` before `=>` in `quote_spanned!` must be an expression: {}",
920			e
921		)
922	});
923	let mut stream = TokenStream2::new();
924	stream.extend(input);
925	env.parse(stream).into()
926}