3
3
use base_db:: { salsa, CrateId , Edition , SourceDatabase } ;
4
4
use either:: Either ;
5
5
use limit:: Limit ;
6
- use mbe:: syntax_node_to_token_tree;
6
+ use mbe:: { syntax_node_to_token_tree, ValueResult } ;
7
7
use rustc_hash:: FxHashSet ;
8
8
use syntax:: {
9
9
ast:: { self , HasAttrs , HasDocComments } ,
@@ -124,16 +124,21 @@ pub trait ExpandDatabase: SourceDatabase {
124
124
fn macro_arg (
125
125
& self ,
126
126
id : MacroCallId ,
127
- ) -> Option < Arc < ( tt:: Subtree , mbe:: TokenMap , fixup:: SyntaxFixupUndoInfo ) > > ;
127
+ ) -> ValueResult <
128
+ Option < Arc < ( tt:: Subtree , mbe:: TokenMap , fixup:: SyntaxFixupUndoInfo ) > > ,
129
+ Arc < Box < [ SyntaxError ] > > ,
130
+ > ;
128
131
/// Extracts syntax node, corresponding to a macro call. That's a firewall
129
132
/// query, only typing in the macro call itself changes the returned
130
133
/// subtree.
131
- fn macro_arg_text ( & self , id : MacroCallId ) -> Option < GreenNode > ;
132
- /// Gets the expander for this macro. This compiles declarative macros, and
133
- /// just fetches procedural ones.
134
- // FIXME: Rename this
134
+ fn macro_arg_node (
135
+ & self ,
136
+ id : MacroCallId ,
137
+ ) -> ValueResult < Option < GreenNode > , Arc < Box < [ SyntaxError ] > > > ;
138
+ /// Fetches the expander for this macro.
135
139
#[ salsa:: transparent]
136
- fn macro_def ( & self , id : MacroDefId ) -> TokenExpander ;
140
+ fn macro_expander ( & self , id : MacroDefId ) -> TokenExpander ;
141
+ /// Fetches (and compiles) the expander of this decl macro.
137
142
fn decl_macro_expander (
138
143
& self ,
139
144
def_crate : CrateId ,
@@ -335,14 +340,20 @@ fn parse_macro_expansion_error(
335
340
fn macro_arg (
336
341
db : & dyn ExpandDatabase ,
337
342
id : MacroCallId ,
338
- ) -> Option < Arc < ( tt:: Subtree , mbe:: TokenMap , fixup:: SyntaxFixupUndoInfo ) > > {
343
+ ) -> ValueResult <
344
+ Option < Arc < ( tt:: Subtree , mbe:: TokenMap , fixup:: SyntaxFixupUndoInfo ) > > ,
345
+ Arc < Box < [ SyntaxError ] > > ,
346
+ > {
339
347
let loc = db. lookup_intern_macro_call ( id) ;
340
348
341
349
if let Some ( EagerCallInfo { arg, arg_id : _, error : _ } ) = loc. eager . as_deref ( ) {
342
- return Some ( Arc :: new ( ( arg. 0 . clone ( ) , arg. 1 . clone ( ) , Default :: default ( ) ) ) ) ;
350
+ return ValueResult :: ok ( Some ( Arc :: new ( ( arg. 0 . clone ( ) , arg. 1 . clone ( ) , Default :: default ( ) ) ) ) ) ;
343
351
}
344
352
345
- let arg = db. macro_arg_text ( id) ?;
353
+ let ValueResult { value, err } = db. macro_arg_node ( id) ;
354
+ let Some ( arg) = value else {
355
+ return ValueResult { value : None , err } ;
356
+ } ;
346
357
347
358
let node = SyntaxNode :: new_root ( arg) ;
348
359
let censor = censor_for_macro_input ( & loc, & node) ;
@@ -360,7 +371,11 @@ fn macro_arg(
360
371
// proc macros expect their inputs without parentheses, MBEs expect it with them included
361
372
tt. delimiter = tt:: Delimiter :: unspecified ( ) ;
362
373
}
363
- Some ( Arc :: new ( ( tt, tmap, fixups. undo_info ) ) )
374
+ let val = Some ( Arc :: new ( ( tt, tmap, fixups. undo_info ) ) ) ;
375
+ match err {
376
+ Some ( err) => ValueResult :: new ( val, err) ,
377
+ None => ValueResult :: ok ( val) ,
378
+ }
364
379
}
365
380
366
381
/// Certain macro calls expect some nodes in the input to be preprocessed away, namely:
@@ -402,9 +417,44 @@ fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet<Sy
402
417
. unwrap_or_default ( )
403
418
}
404
419
405
- fn macro_arg_text ( db : & dyn ExpandDatabase , id : MacroCallId ) -> Option < GreenNode > {
420
+ fn macro_arg_node (
421
+ db : & dyn ExpandDatabase ,
422
+ id : MacroCallId ,
423
+ ) -> ValueResult < Option < GreenNode > , Arc < Box < [ SyntaxError ] > > > {
424
+ let err = || -> Arc < Box < [ _ ] > > {
425
+ Arc :: new ( Box :: new ( [ SyntaxError :: new_at_offset (
426
+ "invalid macro call" . to_owned ( ) ,
427
+ syntax:: TextSize :: from ( 0 ) ,
428
+ ) ] ) )
429
+ } ;
406
430
let loc = db. lookup_intern_macro_call ( id) ;
407
- let arg = loc. kind . arg ( db) ?. value ;
431
+ let arg = if let MacroDefKind :: BuiltInEager ( ..) = loc. def . kind {
432
+ let res = if let Some ( EagerCallInfo { arg, .. } ) = loc. eager . as_deref ( ) {
433
+ Some ( mbe:: token_tree_to_syntax_node ( & arg. 0 , mbe:: TopEntryPoint :: Expr ) . 0 )
434
+ } else {
435
+ loc. kind
436
+ . arg ( db)
437
+ . and_then ( |arg| ast:: TokenTree :: cast ( arg. value ) )
438
+ . map ( |tt| tt. reparse_as_expr ( ) . to_syntax ( ) )
439
+ } ;
440
+
441
+ match res {
442
+ Some ( res) if res. errors ( ) . is_empty ( ) => res. syntax_node ( ) ,
443
+ Some ( res) => {
444
+ return ValueResult :: new (
445
+ Some ( res. syntax_node ( ) . green ( ) . into ( ) ) ,
446
+ // Box::<[_]>::from(res.errors()), not stable yet
447
+ Arc :: new ( res. errors ( ) . to_vec ( ) . into_boxed_slice ( ) ) ,
448
+ ) ;
449
+ }
450
+ None => return ValueResult :: only_err ( err ( ) ) ,
451
+ }
452
+ } else {
453
+ match loc. kind . arg ( db) {
454
+ Some ( res) => res. value ,
455
+ None => return ValueResult :: only_err ( err ( ) ) ,
456
+ }
457
+ } ;
408
458
if matches ! ( loc. kind, MacroCallKind :: FnLike { .. } ) {
409
459
let first = arg. first_child_or_token ( ) . map_or ( T ! [ . ] , |it| it. kind ( ) ) ;
410
460
let last = arg. last_child_or_token ( ) . map_or ( T ! [ . ] , |it| it. kind ( ) ) ;
@@ -419,20 +469,13 @@ fn macro_arg_text(db: &dyn ExpandDatabase, id: MacroCallId) -> Option<GreenNode>
419
469
// Some day, we'll have explicit recursion counters for all
420
470
// recursive things, at which point this code might be removed.
421
471
cov_mark:: hit!( issue9358_bad_macro_stack_overflow) ;
422
- return None ;
472
+ return ValueResult :: only_err ( Arc :: new ( Box :: new ( [ SyntaxError :: new (
473
+ "unbalanced token tree" . to_owned ( ) ,
474
+ arg. text_range ( ) ,
475
+ ) ] ) ) ) ;
423
476
}
424
477
}
425
- if let Some ( EagerCallInfo { arg, .. } ) = loc. eager . as_deref ( ) {
426
- Some (
427
- mbe:: token_tree_to_syntax_node ( & arg. 0 , mbe:: TopEntryPoint :: Expr )
428
- . 0
429
- . syntax_node ( )
430
- . green ( )
431
- . into ( ) ,
432
- )
433
- } else {
434
- Some ( arg. green ( ) . into ( ) )
435
- }
478
+ ValueResult :: ok ( Some ( arg. green ( ) . into ( ) ) )
436
479
}
437
480
438
481
fn decl_macro_expander (
@@ -474,7 +517,7 @@ fn decl_macro_expander(
474
517
Arc :: new ( DeclarativeMacroExpander { mac, def_site_token_map } )
475
518
}
476
519
477
- fn macro_def ( db : & dyn ExpandDatabase , id : MacroDefId ) -> TokenExpander {
520
+ fn macro_expander ( db : & dyn ExpandDatabase , id : MacroDefId ) -> TokenExpander {
478
521
match id. kind {
479
522
MacroDefKind :: Declarative ( ast_id) => {
480
523
TokenExpander :: DeclarativeMacro ( db. decl_macro_expander ( id. krate , ast_id) )
@@ -491,20 +534,10 @@ fn macro_expand(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<Arc<tt
491
534
let _p = profile:: span ( "macro_expand" ) ;
492
535
let loc = db. lookup_intern_macro_call ( id) ;
493
536
494
- // This might look a bit odd, but we do not expand the inputs to eager macros here.
495
- // Eager macros inputs are expanded, well, eagerly when we collect the macro calls.
496
- // That kind of expansion uses the ast id map of an eager macros input though which goes through
497
- // the HirFileId machinery. As eager macro inputs are assigned a macro file id that query
498
- // will end up going through here again, whereas we want to just want to inspect the raw input.
499
- // As such we just return the input subtree here.
500
- if let Some ( EagerCallInfo { arg, arg_id : None , error } ) = loc. eager . as_deref ( ) {
501
- return ExpandResult { value : Arc :: new ( arg. 0 . clone ( ) ) , err : error. clone ( ) } ;
502
- }
503
-
504
- let ( ExpandResult { value : mut tt, mut err } , tmap) = match loc. def . kind {
537
+ let ExpandResult { value : tt, mut err } = match loc. def . kind {
505
538
MacroDefKind :: ProcMacro ( ..) => return db. expand_proc_macro ( id) ,
506
539
MacroDefKind :: BuiltInDerive ( expander, ..) => {
507
- let arg = db. macro_arg_text ( id) . unwrap ( ) ;
540
+ let arg = db. macro_arg_node ( id) . value . unwrap ( ) ;
508
541
509
542
let node = SyntaxNode :: new_root ( arg) ;
510
543
let censor = censor_for_macro_input ( & loc, & node) ;
@@ -520,10 +553,13 @@ fn macro_expand(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<Arc<tt
520
553
521
554
// this cast is a bit sus, can we avoid losing the typedness here?
522
555
let adt = ast:: Adt :: cast ( node) . unwrap ( ) ;
523
- ( expander. expand ( db, id, & adt, & tmap) , Some ( ( tmap, fixups. undo_info ) ) )
556
+ let mut res = expander. expand ( db, id, & adt, & tmap) ;
557
+ fixup:: reverse_fixups ( & mut res. value , & tmap, & fixups. undo_info ) ;
558
+ res
524
559
}
525
560
_ => {
526
- let Some ( macro_arg) = db. macro_arg ( id) else {
561
+ let ValueResult { value, err } = db. macro_arg ( id) ;
562
+ let Some ( macro_arg) = value else {
527
563
return ExpandResult {
528
564
value : Arc :: new ( tt:: Subtree {
529
565
delimiter : tt:: Delimiter :: UNSPECIFIED ,
@@ -534,18 +570,43 @@ fn macro_expand(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<Arc<tt
534
570
err : Some ( ExpandError :: other ( "invalid token tree" ) ) ,
535
571
} ;
536
572
} ;
573
+
537
574
let ( arg, arg_tm, undo_info) = & * macro_arg;
538
575
let mut res = match loc. def . kind {
539
576
MacroDefKind :: Declarative ( id) => {
540
577
db. decl_macro_expander ( loc. def . krate , id) . expand ( arg. clone ( ) )
541
578
}
542
579
MacroDefKind :: BuiltIn ( it, _) => it. expand ( db, id, & arg) . map_err ( Into :: into) ,
580
+ // This might look a bit odd, but we do not expand the inputs to eager macros here.
581
+ // Eager macros inputs are expanded, well, eagerly when we collect the macro calls.
582
+ // That kind of expansion uses the ast id map of an eager macros input though which goes through
583
+ // the HirFileId machinery. As eager macro inputs are assigned a macro file id that query
584
+ // will end up going through here again, whereas we want to just want to inspect the raw input.
585
+ // As such we just return the input subtree here.
586
+ MacroDefKind :: BuiltInEager ( ..) if loc. eager . is_none ( ) => {
587
+ let mut arg = arg. clone ( ) ;
588
+ fixup:: reverse_fixups ( & mut arg, arg_tm, undo_info) ;
589
+
590
+ return ExpandResult {
591
+ value : Arc :: new ( arg) ,
592
+ err : err. map ( |err| {
593
+ let mut buf = String :: new ( ) ;
594
+ for err in & * * err {
595
+ use std:: fmt:: Write ;
596
+ _ = write ! ( buf, "{}, " , err) ;
597
+ }
598
+ buf. pop ( ) ;
599
+ buf. pop ( ) ;
600
+ ExpandError :: other ( buf)
601
+ } ) ,
602
+ } ;
603
+ }
543
604
MacroDefKind :: BuiltInEager ( it, _) => it. expand ( db, id, & arg) . map_err ( Into :: into) ,
544
605
MacroDefKind :: BuiltInAttr ( it, _) => it. expand ( db, id, & arg) ,
545
606
_ => unreachable ! ( ) ,
546
607
} ;
547
608
fixup:: reverse_fixups ( & mut res. value , arg_tm, undo_info) ;
548
- ( res, None )
609
+ res
549
610
}
550
611
} ;
551
612
@@ -559,24 +620,23 @@ fn macro_expand(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<Arc<tt
559
620
return value;
560
621
}
561
622
562
- if let Some ( ( arg_tm, undo_info) ) = & tmap {
563
- fixup:: reverse_fixups ( & mut tt, arg_tm, undo_info) ;
564
- }
565
-
566
623
ExpandResult { value : Arc :: new ( tt) , err }
567
624
}
568
625
569
626
fn expand_proc_macro ( db : & dyn ExpandDatabase , id : MacroCallId ) -> ExpandResult < Arc < tt:: Subtree > > {
570
627
let loc = db. lookup_intern_macro_call ( id) ;
571
- let Some ( macro_arg) = db. macro_arg ( id) else {
628
+ let Some ( macro_arg) = db. macro_arg ( id) . value else {
572
629
return ExpandResult {
573
630
value : Arc :: new ( tt:: Subtree {
574
631
delimiter : tt:: Delimiter :: UNSPECIFIED ,
575
632
token_trees : Vec :: new ( ) ,
576
633
} ) ,
634
+ // FIXME: We should make sure to enforce an invariant that invalid macro
635
+ // calls do not reach this call path!
577
636
err : Some ( ExpandError :: other ( "invalid token tree" ) ) ,
578
637
} ;
579
638
} ;
639
+
580
640
let ( arg_tt, arg_tm, undo_info) = & * macro_arg;
581
641
582
642
let expander = match loc. def . kind {
0 commit comments