@@ -5,7 +5,7 @@ mod source_to_def;
55use std:: {
66 cell:: RefCell ,
77 fmt, iter, mem,
8- ops:: { self , ControlFlow } ,
8+ ops:: { self , ControlFlow , Not } ,
99} ;
1010
1111use base_db:: { FileId , FileRange } ;
@@ -20,16 +20,16 @@ use hir_def::{
2020 AsMacroCall , DefWithBodyId , FieldId , FunctionId , MacroId , TraitId , VariantId ,
2121} ;
2222use 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 ,
2525} ;
2626use itertools:: Itertools ;
2727use rustc_hash:: { FxHashMap , FxHashSet } ;
2828use smallvec:: { smallvec, SmallVec } ;
2929use stdx:: TupleExt ;
3030use syntax:: {
3131 algo:: skip_trivia_token,
32- ast:: { self , HasAttrs as _, HasGenericParams , HasLoopBody , IsString as _} ,
32+ ast:: { self , HasAttrs as _, HasDocComments , HasGenericParams , HasLoopBody , IsString as _} ,
3333 match_ast, AstNode , AstToken , Direction , SyntaxKind , SyntaxNode , SyntaxNodePtr , SyntaxToken ,
3434 TextRange , TextSize ,
3535} ;
@@ -129,9 +129,10 @@ pub struct Semantics<'db, DB> {
129129pub struct SemanticsImpl < ' db > {
130130 pub db : & ' db dyn HirDatabase ,
131131 s2d_cache : RefCell < SourceToDefCache > ,
132- expansion_info_cache : RefCell < FxHashMap < MacroFileId , ExpansionInfo > > ,
133132 /// Rootnode to HirFileId cache
134133 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 > > ,
135136 /// MacroCall to its expansion's MacroFileId cache
136137 macro_call_cache : RefCell < FxHashMap < InFile < ast:: MacroCall > , MacroFileId > > ,
137138}
@@ -616,164 +617,196 @@ impl<'db> SemanticsImpl<'db> {
616617 res
617618 }
618619
619- // FIXME: should only take real file inputs for simplicity
620620 fn descend_into_macros_impl (
621621 & self ,
622622 token : SyntaxToken ,
623623 f : & mut dyn FnMut ( InFile < SyntaxToken > ) -> ControlFlow < ( ) > ,
624624 ) {
625- // FIXME: Clean this up
626625 let _p = profile:: span ( "descend_into_macros" ) ;
627626 let sa = match token. parent ( ) . and_then ( |parent| self . analyze_no_infer ( & parent) ) {
628627 Some ( it) => it,
629628 None => return ,
630629 } ;
631630
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 ;
637636 }
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 ( ) ) ,
643637 } ;
644638
639+ let mut cache = self . expansion_info_cache . borrow_mut ( ) ;
640+ let mut mcache = self . macro_call_cache . borrow_mut ( ) ;
645641 let def_map = sa. resolver . def_map ( ) ;
646- let mut stack: SmallVec < [ _ ; 4 ] > = smallvec ! [ InFile :: new( sa. file_id, token) ] ;
647642
648- let mut process_expansion_for_token = |stack : & mut SmallVec < _ > , macro_file| {
643+ let mut process_expansion_for_token = |stack : & mut Vec < _ > , macro_file| {
649644 let expansion_info = cache
650645 . entry ( macro_file)
651646 . or_insert_with ( || macro_file. expansion_info ( self . db . upcast ( ) ) ) ;
652647
653648 {
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 ( ) ) ;
656651 }
657652
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 ( ) ;
660656
661- // requeue the tokens we got from mapping our current token down
662- stack. extend ( mapped_tokens. map ( Into :: into) ) ;
663657 // 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
665662 } ;
666663
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+ }
687705
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 ( ) ?;
692710
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+ }
699717
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
734727 }
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)
748740 {
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+ }
750764 } 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 ;
752784 }
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
767803 }
768- res
769- } else {
770- None
771- }
772- } ) ( )
773- . is_none ( ) ;
804+ } ) ( )
805+ . is_none ( ) ;
774806
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+ }
777810 }
778811 }
779812 }
0 commit comments