@@ -5,7 +5,7 @@ mod source_to_def;
5
5
use std:: {
6
6
cell:: RefCell ,
7
7
fmt, iter, mem,
8
- ops:: { self , ControlFlow } ,
8
+ ops:: { self , ControlFlow , Not } ,
9
9
} ;
10
10
11
11
use base_db:: { FileId , FileRange } ;
@@ -20,16 +20,16 @@ use hir_def::{
20
20
AsMacroCall , DefWithBodyId , FieldId , FunctionId , MacroId , TraitId , VariantId ,
21
21
} ;
22
22
use hir_expand:: {
23
- db:: ExpandDatabase , files:: InRealFile , name:: AsName , ExpansionInfo , MacroCallId , MacroFileId ,
24
- MacroFileIdExt ,
23
+ db:: ExpandDatabase , files:: InRealFile , name:: AsName , ExpansionInfo , InMacroFile , MacroCallId ,
24
+ MacroFileId , MacroFileIdExt ,
25
25
} ;
26
26
use itertools:: Itertools ;
27
27
use rustc_hash:: { FxHashMap , FxHashSet } ;
28
28
use smallvec:: { smallvec, SmallVec } ;
29
29
use stdx:: TupleExt ;
30
30
use syntax:: {
31
31
algo:: skip_trivia_token,
32
- ast:: { self , HasAttrs as _, HasGenericParams , HasLoopBody , IsString as _} ,
32
+ ast:: { self , HasAttrs as _, HasDocComments , HasGenericParams , HasLoopBody , IsString as _} ,
33
33
match_ast, AstNode , AstToken , Direction , SyntaxKind , SyntaxNode , SyntaxNodePtr , SyntaxToken ,
34
34
TextRange , TextSize ,
35
35
} ;
@@ -129,9 +129,10 @@ pub struct Semantics<'db, DB> {
129
129
pub struct SemanticsImpl < ' db > {
130
130
pub db : & ' db dyn HirDatabase ,
131
131
s2d_cache : RefCell < SourceToDefCache > ,
132
- expansion_info_cache : RefCell < FxHashMap < MacroFileId , ExpansionInfo > > ,
133
132
/// Rootnode to HirFileId cache
134
133
cache : RefCell < FxHashMap < SyntaxNode , HirFileId > > ,
134
+ // These 2 caches are mainly useful for semantic highlighting as nothing else descends a lot of tokens
135
+ expansion_info_cache : RefCell < FxHashMap < MacroFileId , ExpansionInfo > > ,
135
136
/// MacroCall to its expansion's MacroFileId cache
136
137
macro_call_cache : RefCell < FxHashMap < InFile < ast:: MacroCall > , MacroFileId > > ,
137
138
}
@@ -616,164 +617,196 @@ impl<'db> SemanticsImpl<'db> {
616
617
res
617
618
}
618
619
619
- // FIXME: should only take real file inputs for simplicity
620
620
fn descend_into_macros_impl (
621
621
& self ,
622
622
token : SyntaxToken ,
623
623
f : & mut dyn FnMut ( InFile < SyntaxToken > ) -> ControlFlow < ( ) > ,
624
624
) {
625
- // FIXME: Clean this up
626
625
let _p = profile:: span ( "descend_into_macros" ) ;
627
626
let sa = match token. parent ( ) . and_then ( |parent| self . analyze_no_infer ( & parent) ) {
628
627
Some ( it) => it,
629
628
None => return ,
630
629
} ;
631
630
632
- let mut cache = self . expansion_info_cache . borrow_mut ( ) ;
633
- let mut mcache = self . macro_call_cache . borrow_mut ( ) ;
634
- let span = match sa . file_id . repr ( ) {
635
- base_db :: span :: HirFileIdRepr :: FileId ( file_id ) => {
636
- self . db . real_span_map ( file_id ) . span_for_range ( token . text_range ( ) )
631
+ let span = match sa . file_id . file_id ( ) {
632
+ Some ( file_id ) => self . db . real_span_map ( file_id ) . span_for_range ( token . text_range ( ) ) ,
633
+ None => {
634
+ stdx :: never! ( ) ;
635
+ return ;
637
636
}
638
- base_db:: span:: HirFileIdRepr :: MacroFile ( macro_file) => cache
639
- . entry ( macro_file)
640
- . or_insert_with ( || macro_file. expansion_info ( self . db . upcast ( ) ) )
641
- . exp_map
642
- . span_at ( token. text_range ( ) . start ( ) ) ,
643
637
} ;
644
638
639
+ let mut cache = self . expansion_info_cache . borrow_mut ( ) ;
640
+ let mut mcache = self . macro_call_cache . borrow_mut ( ) ;
645
641
let def_map = sa. resolver . def_map ( ) ;
646
- let mut stack: SmallVec < [ _ ; 4 ] > = smallvec ! [ InFile :: new( sa. file_id, token) ] ;
647
642
648
- let mut process_expansion_for_token = |stack : & mut SmallVec < _ > , macro_file| {
643
+ let mut process_expansion_for_token = |stack : & mut Vec < _ > , macro_file| {
649
644
let expansion_info = cache
650
645
. entry ( macro_file)
651
646
. or_insert_with ( || macro_file. expansion_info ( self . db . upcast ( ) ) ) ;
652
647
653
648
{
654
- let InFile { file_id, value } = expansion_info. expanded ( ) ;
655
- self . cache ( value, file_id) ;
649
+ let InMacroFile { file_id, value } = expansion_info. expanded ( ) ;
650
+ self . cache ( value, file_id. into ( ) ) ;
656
651
}
657
652
658
- let mapped_tokens = expansion_info. map_range_down ( span) ?;
659
- let len = stack. len ( ) ;
653
+ let InMacroFile { file_id, value : mapped_tokens } =
654
+ expansion_info. map_range_down ( span) ?;
655
+ let mapped_tokens: SmallVec < [ _ ; 2 ] > = mapped_tokens. collect ( ) ;
660
656
661
- // requeue the tokens we got from mapping our current token down
662
- stack. extend ( mapped_tokens. map ( Into :: into) ) ;
663
657
// if the length changed we have found a mapping for the token
664
- ( stack. len ( ) != len) . then_some ( ( ) )
658
+ let res = mapped_tokens. is_empty ( ) . not ( ) . then_some ( ( ) ) ;
659
+ // requeue the tokens we got from mapping our current token down
660
+ stack. push ( ( HirFileId :: from ( file_id) , mapped_tokens) ) ;
661
+ res
665
662
} ;
666
663
667
- // Remap the next token in the queue into a macro call its in, if it is not being remapped
668
- // either due to not being in a macro-call or because its unused push it into the result vec,
669
- // otherwise push the remapped tokens back into the queue as they can potentially be remapped again.
670
- while let Some ( token) = stack. pop ( ) {
671
- let was_not_remapped = ( || {
672
- // First expand into attribute invocations
673
-
674
- let containing_attribute_macro_call = self . with_ctx ( |ctx| {
675
- token. value . parent_ancestors ( ) . filter_map ( ast:: Item :: cast) . find_map ( |item| {
676
- if item. attrs ( ) . next ( ) . is_none ( ) {
677
- // Don't force populate the dyn cache for items that don't have an attribute anyways
678
- return None ;
679
- }
680
- Some ( ctx. item_to_macro_call ( token. with_value ( item. clone ( ) ) ) ?)
681
- } )
682
- } ) ;
683
- if let Some ( call_id) = containing_attribute_macro_call {
684
- let file_id = call_id. as_macro_file ( ) ;
685
- return process_expansion_for_token ( & mut stack, file_id) ;
686
- }
664
+ let mut stack: Vec < ( _ , SmallVec < [ _ ; 2 ] > ) > = vec ! [ ( sa. file_id, smallvec![ token] ) ] ;
665
+
666
+ while let Some ( ( file_id, mut tokens) ) = stack. pop ( ) {
667
+ while let Some ( token) = tokens. pop ( ) {
668
+ let was_not_remapped = ( || {
669
+ // First expand into attribute invocations
670
+ let containing_attribute_macro_call = self . with_ctx ( |ctx| {
671
+ token. parent_ancestors ( ) . filter_map ( ast:: Item :: cast) . find_map ( |item| {
672
+ if item. attrs ( ) . next ( ) . is_none ( ) {
673
+ // Don't force populate the dyn cache for items that don't have an attribute anyways
674
+ return None ;
675
+ }
676
+ Some ( (
677
+ ctx. item_to_macro_call ( InFile :: new ( file_id, item. clone ( ) ) ) ?,
678
+ item,
679
+ ) )
680
+ } )
681
+ } ) ;
682
+ if let Some ( ( call_id, item) ) = containing_attribute_macro_call {
683
+ let file_id = call_id. as_macro_file ( ) ;
684
+ let attr_id = match self . db . lookup_intern_macro_call ( call_id) . kind {
685
+ hir_expand:: MacroCallKind :: Attr { invoc_attr_index, .. } => {
686
+ invoc_attr_index. ast_index ( )
687
+ }
688
+ _ => 0 ,
689
+ } ;
690
+ let text_range = item. syntax ( ) . text_range ( ) ;
691
+ let start = item
692
+ . doc_comments_and_attrs ( )
693
+ . nth ( attr_id)
694
+ . map ( |attr| match attr {
695
+ Either :: Left ( it) => it. syntax ( ) . text_range ( ) . start ( ) ,
696
+ Either :: Right ( it) => it. syntax ( ) . text_range ( ) . start ( ) ,
697
+ } )
698
+ . unwrap_or_else ( || text_range. start ( ) ) ;
699
+ let text_range = TextRange :: new ( start, text_range. end ( ) ) ;
700
+ // remove any other token in this macro input, all their mappings are the
701
+ // same as this one
702
+ tokens. retain ( |t| !text_range. contains_range ( t. text_range ( ) ) ) ;
703
+ return process_expansion_for_token ( & mut stack, file_id) ;
704
+ }
687
705
688
- // Then check for token trees, that means we are either in a function-like macro or
689
- // secondary attribute inputs
690
- let tt = token. value . parent_ancestors ( ) . map_while ( ast:: TokenTree :: cast) . last ( ) ?;
691
- let parent = tt. syntax ( ) . parent ( ) ?;
706
+ // Then check for token trees, that means we are either in a function-like macro or
707
+ // secondary attribute inputs
708
+ let tt = token. parent_ancestors ( ) . map_while ( ast:: TokenTree :: cast) . last ( ) ?;
709
+ let parent = tt. syntax ( ) . parent ( ) ?;
692
710
693
- if tt. left_delimiter_token ( ) . map_or ( false , |it| it == token. value ) {
694
- return None ;
695
- }
696
- if tt. right_delimiter_token ( ) . map_or ( false , |it| it == token. value ) {
697
- return None ;
698
- }
711
+ if tt. left_delimiter_token ( ) . map_or ( false , |it| it == token) {
712
+ return None ;
713
+ }
714
+ if tt. right_delimiter_token ( ) . map_or ( false , |it| it == token) {
715
+ return None ;
716
+ }
699
717
700
- if let Some ( macro_call) = ast:: MacroCall :: cast ( parent. clone ( ) ) {
701
- let mcall: hir_expand:: files:: InFileWrapper < HirFileId , ast:: MacroCall > =
702
- token. with_value ( macro_call) ;
703
- let file_id = match mcache. get ( & mcall) {
704
- Some ( & it) => it,
705
- None => {
706
- let it = sa. expand ( self . db , mcall. as_ref ( ) ) ?;
707
- mcache. insert ( mcall, it) ;
708
- it
709
- }
710
- } ;
711
- process_expansion_for_token ( & mut stack, file_id)
712
- } else if let Some ( meta) = ast:: Meta :: cast ( parent) {
713
- // attribute we failed expansion for earlier, this might be a derive invocation
714
- // or derive helper attribute
715
- let attr = meta. parent_attr ( ) ?;
716
-
717
- let adt = if let Some ( adt) = attr. syntax ( ) . parent ( ) . and_then ( ast:: Adt :: cast) {
718
- // this might be a derive, or a derive helper on an ADT
719
- let derive_call = self . with_ctx ( |ctx| {
720
- // so try downmapping the token into the pseudo derive expansion
721
- // see [hir_expand::builtin_attr_macro] for how the pseudo derive expansion works
722
- ctx. attr_to_derive_macro_call (
723
- token. with_value ( & adt) ,
724
- token. with_value ( attr. clone ( ) ) ,
725
- )
726
- . map ( |( _, call_id, _) | call_id)
727
- } ) ;
728
-
729
- match derive_call {
730
- Some ( call_id) => {
731
- // resolved to a derive
732
- let file_id = call_id. as_macro_file ( ) ;
733
- return process_expansion_for_token ( & mut stack, file_id) ;
718
+ if let Some ( macro_call) = ast:: MacroCall :: cast ( parent. clone ( ) ) {
719
+ let mcall: hir_expand:: files:: InFileWrapper < HirFileId , ast:: MacroCall > =
720
+ InFile :: new ( file_id, macro_call) ;
721
+ let file_id = match mcache. get ( & mcall) {
722
+ Some ( & it) => it,
723
+ None => {
724
+ let it = sa. expand ( self . db , mcall. as_ref ( ) ) ?;
725
+ mcache. insert ( mcall, it) ;
726
+ it
734
727
}
735
- None => Some ( adt) ,
736
- }
737
- } else {
738
- // Otherwise this could be a derive helper on a variant or field
739
- if let Some ( field) = attr. syntax ( ) . parent ( ) . and_then ( ast:: RecordField :: cast)
740
- {
741
- field. syntax ( ) . ancestors ( ) . take ( 4 ) . find_map ( ast:: Adt :: cast)
742
- } else if let Some ( field) =
743
- attr. syntax ( ) . parent ( ) . and_then ( ast:: TupleField :: cast)
744
- {
745
- field. syntax ( ) . ancestors ( ) . take ( 4 ) . find_map ( ast:: Adt :: cast)
746
- } else if let Some ( variant) =
747
- attr. syntax ( ) . parent ( ) . and_then ( ast:: Variant :: cast)
728
+ } ;
729
+ let text_range = tt. syntax ( ) . text_range ( ) ;
730
+ // remove any other token in this macro input, all their mappings are the
731
+ // same as this one
732
+ tokens. retain ( |t| !text_range. contains_range ( t. text_range ( ) ) ) ;
733
+ process_expansion_for_token ( & mut stack, file_id)
734
+ } else if let Some ( meta) = ast:: Meta :: cast ( parent) {
735
+ // attribute we failed expansion for earlier, this might be a derive invocation
736
+ // or derive helper attribute
737
+ let attr = meta. parent_attr ( ) ?;
738
+
739
+ let adt = if let Some ( adt) = attr. syntax ( ) . parent ( ) . and_then ( ast:: Adt :: cast)
748
740
{
749
- variant. syntax ( ) . ancestors ( ) . nth ( 2 ) . and_then ( ast:: Adt :: cast)
741
+ // this might be a derive, or a derive helper on an ADT
742
+ let derive_call = self . with_ctx ( |ctx| {
743
+ // so try downmapping the token into the pseudo derive expansion
744
+ // see [hir_expand::builtin_attr_macro] for how the pseudo derive expansion works
745
+ ctx. attr_to_derive_macro_call (
746
+ InFile :: new ( file_id, & adt) ,
747
+ InFile :: new ( file_id, attr. clone ( ) ) ,
748
+ )
749
+ . map ( |( _, call_id, _) | call_id)
750
+ } ) ;
751
+
752
+ match derive_call {
753
+ Some ( call_id) => {
754
+ // resolved to a derive
755
+ let file_id = call_id. as_macro_file ( ) ;
756
+ let text_range = attr. syntax ( ) . text_range ( ) ;
757
+ // remove any other token in this macro input, all their mappings are the
758
+ // same as this one
759
+ tokens. retain ( |t| !text_range. contains_range ( t. text_range ( ) ) ) ;
760
+ return process_expansion_for_token ( & mut stack, file_id) ;
761
+ }
762
+ None => Some ( adt) ,
763
+ }
750
764
} else {
751
- None
765
+ // Otherwise this could be a derive helper on a variant or field
766
+ if let Some ( field) =
767
+ attr. syntax ( ) . parent ( ) . and_then ( ast:: RecordField :: cast)
768
+ {
769
+ field. syntax ( ) . ancestors ( ) . take ( 4 ) . find_map ( ast:: Adt :: cast)
770
+ } else if let Some ( field) =
771
+ attr. syntax ( ) . parent ( ) . and_then ( ast:: TupleField :: cast)
772
+ {
773
+ field. syntax ( ) . ancestors ( ) . take ( 4 ) . find_map ( ast:: Adt :: cast)
774
+ } else if let Some ( variant) =
775
+ attr. syntax ( ) . parent ( ) . and_then ( ast:: Variant :: cast)
776
+ {
777
+ variant. syntax ( ) . ancestors ( ) . nth ( 2 ) . and_then ( ast:: Adt :: cast)
778
+ } else {
779
+ None
780
+ }
781
+ } ?;
782
+ if !self . with_ctx ( |ctx| ctx. has_derives ( InFile :: new ( file_id, & adt) ) ) {
783
+ return None ;
752
784
}
753
- } ?;
754
- if !self . with_ctx ( |ctx| ctx. has_derives ( InFile :: new ( token. file_id , & adt) ) ) {
755
- return None ;
756
- }
757
- // Not an attribute, nor a derive, so it's either a builtin or a derive helper
758
- // Try to resolve to a derive helper and downmap
759
- let attr_name = attr. path ( ) . and_then ( |it| it. as_single_name_ref ( ) ) ?. as_name ( ) ;
760
- let id = self . db . ast_id_map ( token. file_id ) . ast_id ( & adt) ;
761
- let helpers =
762
- def_map. derive_helpers_in_scope ( InFile :: new ( token. file_id , id) ) ?;
763
- let mut res = None ;
764
- for ( .., derive) in helpers. iter ( ) . filter ( |( helper, ..) | * helper == attr_name) {
765
- res =
766
- res. or ( process_expansion_for_token ( & mut stack, derive. as_macro_file ( ) ) ) ;
785
+ // Not an attribute, nor a derive, so it's either a builtin or a derive helper
786
+ // Try to resolve to a derive helper and downmap
787
+ let attr_name =
788
+ attr. path ( ) . and_then ( |it| it. as_single_name_ref ( ) ) ?. as_name ( ) ;
789
+ let id = self . db . ast_id_map ( file_id) . ast_id ( & adt) ;
790
+ let helpers = def_map. derive_helpers_in_scope ( InFile :: new ( file_id, id) ) ?;
791
+ let mut res = None ;
792
+ for ( .., derive) in
793
+ helpers. iter ( ) . filter ( |( helper, ..) | * helper == attr_name)
794
+ {
795
+ res = res. or ( process_expansion_for_token (
796
+ & mut stack,
797
+ derive. as_macro_file ( ) ,
798
+ ) ) ;
799
+ }
800
+ res
801
+ } else {
802
+ None
767
803
}
768
- res
769
- } else {
770
- None
771
- }
772
- } ) ( )
773
- . is_none ( ) ;
804
+ } ) ( )
805
+ . is_none ( ) ;
774
806
775
- if was_not_remapped && f ( token) . is_break ( ) {
776
- break ;
807
+ if was_not_remapped && f ( InFile :: new ( file_id, token) ) . is_break ( ) {
808
+ break ;
809
+ }
777
810
}
778
811
}
779
812
}
0 commit comments