|
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