|
1 | 1 | //! Builtin macro |
2 | 2 |
|
3 | | -use std::mem; |
4 | | - |
5 | | -use ::tt::Ident; |
6 | 3 | use base_db::{AnchoredPath, Edition, FileId}; |
7 | 4 | use cfg::CfgExpr; |
8 | 5 | use either::Either; |
9 | 6 | use mbe::{parse_exprs_with_sep, parse_to_token_tree, TokenMap}; |
10 | | -use rustc_hash::FxHashMap; |
11 | 7 | use syntax::{ |
12 | 8 | ast::{self, AstToken}, |
13 | 9 | SmolStr, |
@@ -97,11 +93,11 @@ register_builtin! { |
97 | 93 | (unreachable, Unreachable) => unreachable_expand, |
98 | 94 | (log_syntax, LogSyntax) => log_syntax_expand, |
99 | 95 | (trace_macros, TraceMacros) => trace_macros_expand, |
100 | | - |
101 | | - EAGER: |
102 | 96 | (format_args, FormatArgs) => format_args_expand, |
103 | 97 | (const_format_args, ConstFormatArgs) => format_args_expand, |
104 | 98 | (format_args_nl, FormatArgsNl) => format_args_nl_expand, |
| 99 | + |
| 100 | + EAGER: |
105 | 101 | (compile_error, CompileError) => compile_error_expand, |
106 | 102 | (concat, Concat) => concat_expand, |
107 | 103 | (concat_idents, ConcatIdents) => concat_idents_expand, |
@@ -247,160 +243,22 @@ fn format_args_expand_general( |
247 | 243 | _db: &dyn ExpandDatabase, |
248 | 244 | _id: MacroCallId, |
249 | 245 | tt: &tt::Subtree, |
250 | | - end_string: &str, |
| 246 | + // FIXME: Make use of this so that mir interpretation works properly |
| 247 | + _end_string: &str, |
251 | 248 | ) -> ExpandResult<tt::Subtree> { |
252 | | - let args = parse_exprs_with_sep(tt, ','); |
253 | | - |
254 | | - let expand_error = |
255 | | - ExpandResult::new(tt::Subtree::empty(), mbe::ExpandError::NoMatchingRule.into()); |
256 | | - |
257 | | - let mut key_args = FxHashMap::default(); |
258 | | - let mut args = args.into_iter().filter_map(|mut arg| { |
259 | | - // Remove `key =`. |
260 | | - if matches!(arg.token_trees.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=') |
261 | | - { |
262 | | - // but not with `==` |
263 | | - if !matches!(arg.token_trees.get(2), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=') |
264 | | - { |
265 | | - let key = arg.token_trees.drain(..2).next().unwrap(); |
266 | | - key_args.insert(key.to_string(), arg); |
267 | | - return None; |
268 | | - } |
269 | | - } |
270 | | - Some(arg) |
271 | | - }).collect::<Vec<_>>().into_iter(); |
272 | | - // ^^^^^^^ we need this collect, to enforce the side effect of the filter_map closure (building the `key_args`) |
273 | | - let Some(format_subtree) = args.next() else { |
274 | | - return expand_error; |
275 | | - }; |
276 | | - let format_string = (|| { |
277 | | - let token_tree = format_subtree.token_trees.get(0)?; |
278 | | - match token_tree { |
279 | | - tt::TokenTree::Leaf(l) => match l { |
280 | | - tt::Leaf::Literal(l) => { |
281 | | - if let Some(mut text) = l.text.strip_prefix('r') { |
282 | | - let mut raw_sharps = String::new(); |
283 | | - while let Some(t) = text.strip_prefix('#') { |
284 | | - text = t; |
285 | | - raw_sharps.push('#'); |
286 | | - } |
287 | | - text = |
288 | | - text.strip_suffix(&raw_sharps)?.strip_prefix('"')?.strip_suffix('"')?; |
289 | | - Some((text, l.span, Some(raw_sharps))) |
290 | | - } else { |
291 | | - let text = l.text.strip_prefix('"')?.strip_suffix('"')?; |
292 | | - let span = l.span; |
293 | | - Some((text, span, None)) |
294 | | - } |
295 | | - } |
296 | | - _ => None, |
297 | | - }, |
298 | | - tt::TokenTree::Subtree(_) => None, |
299 | | - } |
300 | | - })(); |
301 | | - let Some((format_string, _format_string_span, raw_sharps)) = format_string else { |
302 | | - return expand_error; |
303 | | - }; |
304 | | - let mut format_iter = format_string.chars().peekable(); |
305 | | - let mut parts = vec![]; |
306 | | - let mut last_part = String::new(); |
307 | | - let mut arg_tts = vec![]; |
308 | | - let mut err = None; |
309 | | - while let Some(c) = format_iter.next() { |
310 | | - // Parsing the format string. See https://doc.rust-lang.org/std/fmt/index.html#syntax for the grammar and more info |
311 | | - match c { |
312 | | - '{' => { |
313 | | - if format_iter.peek() == Some(&'{') { |
314 | | - format_iter.next(); |
315 | | - last_part.push('{'); |
316 | | - continue; |
317 | | - } |
318 | | - let mut argument = String::new(); |
319 | | - while ![Some(&'}'), Some(&':')].contains(&format_iter.peek()) { |
320 | | - argument.push(match format_iter.next() { |
321 | | - Some(c) => c, |
322 | | - None => return expand_error, |
323 | | - }); |
324 | | - } |
325 | | - let format_spec = match format_iter.next().unwrap() { |
326 | | - '}' => "".to_owned(), |
327 | | - ':' => { |
328 | | - let mut s = String::new(); |
329 | | - while let Some(c) = format_iter.next() { |
330 | | - if c == '}' { |
331 | | - break; |
332 | | - } |
333 | | - s.push(c); |
334 | | - } |
335 | | - s |
336 | | - } |
337 | | - _ => unreachable!(), |
338 | | - }; |
339 | | - parts.push(mem::take(&mut last_part)); |
340 | | - let arg_tree = if argument.is_empty() { |
341 | | - match args.next() { |
342 | | - Some(it) => it, |
343 | | - None => { |
344 | | - err = Some(mbe::ExpandError::NoMatchingRule.into()); |
345 | | - tt::Subtree::empty() |
346 | | - } |
347 | | - } |
348 | | - } else if let Some(tree) = key_args.get(&argument) { |
349 | | - tree.clone() |
350 | | - } else { |
351 | | - // FIXME: we should pick the related substring of the `_format_string_span` as the span. You |
352 | | - // can use `.char_indices()` instead of `.char()` for `format_iter` to find the substring interval. |
353 | | - let ident = Ident::new(argument, tt::TokenId::unspecified()); |
354 | | - quote!(#ident) |
355 | | - }; |
356 | | - let formatter = match &*format_spec { |
357 | | - "?" => quote!(::core::fmt::Debug::fmt), |
358 | | - "" => quote!(::core::fmt::Display::fmt), |
359 | | - _ => { |
360 | | - // FIXME: implement the rest and return expand error here |
361 | | - quote!(::core::fmt::Display::fmt) |
362 | | - } |
363 | | - }; |
364 | | - arg_tts.push(quote! { ::core::fmt::ArgumentV1::new(&(#arg_tree), #formatter), }); |
365 | | - } |
366 | | - '}' => { |
367 | | - if format_iter.peek() == Some(&'}') { |
368 | | - format_iter.next(); |
369 | | - last_part.push('}'); |
370 | | - } else { |
371 | | - return expand_error; |
372 | | - } |
373 | | - } |
374 | | - _ => last_part.push(c), |
375 | | - } |
376 | | - } |
377 | | - last_part += end_string; |
378 | | - if !last_part.is_empty() { |
379 | | - parts.push(last_part); |
380 | | - } |
381 | | - let part_tts = parts.into_iter().map(|it| { |
382 | | - let text = if let Some(raw) = &raw_sharps { |
383 | | - format!("r{raw}\"{}\"{raw}", it).into() |
384 | | - } else { |
385 | | - format!("\"{}\"", it).into() |
386 | | - }; |
387 | | - let l = tt::Literal { span: tt::TokenId::unspecified(), text }; |
388 | | - quote!(#l ,) |
| 249 | + let pound = quote! {@PUNCT '#'}; |
| 250 | + let mut tt = tt.clone(); |
| 251 | + tt.delimiter.kind = tt::DelimiterKind::Parenthesis; |
| 252 | + return ExpandResult::ok(quote! { |
| 253 | + builtin #pound format_args #tt |
389 | 254 | }); |
390 | | - let arg_tts = arg_tts.into_iter().flat_map(|arg| arg.token_trees); |
391 | | - let expanded = quote! { |
392 | | - ::core::fmt::Arguments::new_v1(&[##part_tts], &[##arg_tts]) |
393 | | - }; |
394 | | - ExpandResult { value: expanded, err } |
395 | 255 | } |
396 | 256 |
|
397 | 257 | fn asm_expand( |
398 | 258 | _db: &dyn ExpandDatabase, |
399 | 259 | _id: MacroCallId, |
400 | 260 | tt: &tt::Subtree, |
401 | 261 | ) -> ExpandResult<tt::Subtree> { |
402 | | - // FIXME: parse asm here |
403 | | - |
404 | 262 | // We expand all assembly snippets to `format_args!` invocations to get format syntax |
405 | 263 | // highlighting for them. |
406 | 264 |
|
|
0 commit comments