1 // NOTE: Most code in this file is taken straight from `thiserror`.
2 use std::collections::HashSet as Set;
3 use std::iter::FromIterator;
4
5 use proc_macro2::{Delimiter, Group, TokenStream, TokenTree};
6 use quote::{format_ident, quote, quote_spanned, ToTokens};
7 use syn::ext::IdentExt;
8 use syn::parse::{ParseStream, Parser};
9 use syn::{braced, bracketed, parenthesized, Ident, Index, LitStr, Member, Result, Token};
10
11 #[derive(Clone)]
12 pub struct Display {
13 pub fmt: LitStr,
14 pub args: TokenStream,
15 pub has_bonus_display: bool,
16 }
17
18 impl ToTokens for Display {
to_tokens(&self, tokens: &mut TokenStream)19 fn to_tokens(&self, tokens: &mut TokenStream) {
20 let fmt = &self.fmt;
21 let args = &self.args;
22 tokens.extend(quote! {
23 write!(__formatter, #fmt #args)
24 });
25 }
26 }
27
28 impl Display {
29 // Transform `"error {var}"` to `"error {}", var`.
expand_shorthand(&mut self, members: &Set<Member>)30 pub fn expand_shorthand(&mut self, members: &Set<Member>) {
31 let raw_args = self.args.clone();
32 let mut named_args = explicit_named_args.parse2(raw_args).unwrap();
33
34 let span = self.fmt.span();
35 let fmt = self.fmt.value();
36 let mut read = fmt.as_str();
37 let mut out = String::new();
38 let mut args = self.args.clone();
39 let mut has_bonus_display = false;
40
41 let mut has_trailing_comma = false;
42 if let Some(TokenTree::Punct(punct)) = args.clone().into_iter().last() {
43 if punct.as_char() == ',' {
44 has_trailing_comma = true;
45 }
46 }
47
48 while let Some(brace) = read.find('{') {
49 out += &read[..brace + 1];
50 read = &read[brace + 1..];
51 if read.starts_with('{') {
52 out.push('{');
53 read = &read[1..];
54 continue;
55 }
56 let next = match read.chars().next() {
57 Some(next) => next,
58 None => return,
59 };
60 let member = match next {
61 '0'..='9' => {
62 let int = take_int(&mut read);
63 let member = match int.parse::<u32>() {
64 Ok(index) => Member::Unnamed(Index { index, span }),
65 Err(_) => return,
66 };
67 if !members.contains(&member) {
68 out += ∫
69 continue;
70 }
71 member
72 }
73 'a'..='z' | 'A'..='Z' | '_' => {
74 let mut ident = take_ident(&mut read);
75 ident.set_span(span);
76 Member::Named(ident)
77 }
78 _ => continue,
79 };
80 let local = match &member {
81 Member::Unnamed(index) => format_ident!("_{}", index),
82 Member::Named(ident) => ident.clone(),
83 };
84 let mut formatvar = local.clone();
85 if formatvar.to_string().starts_with("r#") {
86 formatvar = format_ident!("r_{}", formatvar);
87 }
88 if formatvar.to_string().starts_with('_') {
89 // Work around leading underscore being rejected by 1.40 and
90 // older compilers. https://github.com/rust-lang/rust/pull/66847
91 formatvar = format_ident!("field_{}", formatvar);
92 }
93 out += &formatvar.to_string();
94 if !named_args.insert(formatvar.clone()) {
95 // Already specified in the format argument list.
96 continue;
97 }
98 if !has_trailing_comma {
99 args.extend(quote_spanned!(span=> ,));
100 }
101 args.extend(quote_spanned!(span=> #formatvar = #local));
102 if read.starts_with('}') && members.contains(&member) {
103 has_bonus_display = true;
104 // args.extend(quote_spanned!(span=> .as_display()));
105 }
106 has_trailing_comma = false;
107 }
108
109 out += read;
110 self.fmt = LitStr::new(&out, self.fmt.span());
111 self.args = args;
112 self.has_bonus_display = has_bonus_display;
113 }
114 }
115
explicit_named_args(input: ParseStream) -> Result<Set<Ident>>116 fn explicit_named_args(input: ParseStream) -> Result<Set<Ident>> {
117 let mut named_args = Set::new();
118
119 while !input.is_empty() {
120 if input.peek(Token![,]) && input.peek2(Ident::peek_any) && input.peek3(Token![=]) {
121 input.parse::<Token![,]>()?;
122 let ident = input.call(Ident::parse_any)?;
123 input.parse::<Token![=]>()?;
124 named_args.insert(ident);
125 } else {
126 input.parse::<TokenTree>()?;
127 }
128 }
129
130 Ok(named_args)
131 }
132
take_int(read: &mut &str) -> String133 fn take_int(read: &mut &str) -> String {
134 let mut int = String::new();
135 for (i, ch) in read.char_indices() {
136 match ch {
137 '0'..='9' => int.push(ch),
138 _ => {
139 *read = &read[i..];
140 break;
141 }
142 }
143 }
144 int
145 }
146
take_ident(read: &mut &str) -> Ident147 fn take_ident(read: &mut &str) -> Ident {
148 let mut ident = String::new();
149 let raw = read.starts_with("r#");
150 if raw {
151 ident.push_str("r#");
152 *read = &read[2..];
153 }
154 for (i, ch) in read.char_indices() {
155 match ch {
156 'a'..='z' | 'A'..='Z' | '0'..='9' | '_' => ident.push(ch),
157 _ => {
158 *read = &read[i..];
159 break;
160 }
161 }
162 }
163 Ident::parse_any.parse_str(&ident).unwrap()
164 }
165
parse_token_expr(input: ParseStream, mut begin_expr: bool) -> Result<TokenStream>166 pub fn parse_token_expr(input: ParseStream, mut begin_expr: bool) -> Result<TokenStream> {
167 let mut tokens = Vec::new();
168 while !input.is_empty() {
169 if begin_expr && input.peek(Token![.]) {
170 if input.peek2(Ident) {
171 input.parse::<Token![.]>()?;
172 begin_expr = false;
173 continue;
174 }
175 if input.peek2(syn::LitInt) {
176 input.parse::<Token![.]>()?;
177 let int: Index = input.parse()?;
178 let ident = format_ident!("_{}", int.index, span = int.span);
179 tokens.push(TokenTree::Ident(ident));
180 begin_expr = false;
181 continue;
182 }
183 }
184
185 begin_expr = input.peek(Token![break])
186 || input.peek(Token![continue])
187 || input.peek(Token![if])
188 || input.peek(Token![in])
189 || input.peek(Token![match])
190 || input.peek(Token![mut])
191 || input.peek(Token![return])
192 || input.peek(Token![while])
193 || input.peek(Token![+])
194 || input.peek(Token![&])
195 || input.peek(Token![!])
196 || input.peek(Token![^])
197 || input.peek(Token![,])
198 || input.peek(Token![/])
199 || input.peek(Token![=])
200 || input.peek(Token![>])
201 || input.peek(Token![<])
202 || input.peek(Token![|])
203 || input.peek(Token![%])
204 || input.peek(Token![;])
205 || input.peek(Token![*])
206 || input.peek(Token![-]);
207
208 let token: TokenTree = if input.peek(syn::token::Paren) {
209 let content;
210 let delimiter = parenthesized!(content in input);
211 let nested = parse_token_expr(&content, true)?;
212 let mut group = Group::new(Delimiter::Parenthesis, nested);
213 group.set_span(delimiter.span.join());
214 TokenTree::Group(group)
215 } else if input.peek(syn::token::Brace) {
216 let content;
217 let delimiter = braced!(content in input);
218 let nested = parse_token_expr(&content, true)?;
219 let mut group = Group::new(Delimiter::Brace, nested);
220 group.set_span(delimiter.span.join());
221 TokenTree::Group(group)
222 } else if input.peek(syn::token::Bracket) {
223 let content;
224 let delimiter = bracketed!(content in input);
225 let nested = parse_token_expr(&content, true)?;
226 let mut group = Group::new(Delimiter::Bracket, nested);
227 group.set_span(delimiter.span.join());
228 TokenTree::Group(group)
229 } else {
230 input.parse()?
231 };
232 tokens.push(token);
233 }
234 Ok(TokenStream::from_iter(tokens))
235 }
236