@@ -733,6 +733,8 @@ impl<'db> SemanticsImpl<'db> {
733733 Some ( it) => it,
734734 None => return ,
735735 } ;
736+ let def_map = sa. resolver . def_map ( ) ;
737+
736738 let mut stack: SmallVec < [ _ ; 4 ] > = smallvec ! [ InFile :: new( sa. file_id, token) ] ;
737739 let mut cache = self . expansion_info_cache . borrow_mut ( ) ;
738740 let mut mcache = self . macro_call_cache . borrow_mut ( ) ;
@@ -764,7 +766,7 @@ impl<'db> SemanticsImpl<'db> {
764766 while let Some ( token) = stack. pop ( ) {
765767 self . db . unwind_if_cancelled ( ) ;
766768 let was_not_remapped = ( || {
767- // are we inside an attribute macro call
769+ // First expand into attribute invocations
768770 let containing_attribute_macro_call = self . with_ctx ( |ctx| {
769771 token. value . parent_ancestors ( ) . filter_map ( ast:: Item :: cast) . find_map ( |item| {
770772 if item. attrs ( ) . next ( ) . is_none ( ) {
@@ -784,53 +786,19 @@ impl<'db> SemanticsImpl<'db> {
784786 ) ;
785787 }
786788
787- // or are we inside a function-like macro call
788- if let Some ( tt) =
789- // FIXME replace map.while_some with take_while once stable
790- token
791- . value
792- . parent_ancestors ( )
793- . map ( ast:: TokenTree :: cast)
794- . while_some ( )
795- . last ( )
796- {
797- let parent = tt. syntax ( ) . parent ( ) ?;
798- // check for derive attribute here
799- let macro_call = match_ast ! {
800- match parent {
801- ast:: MacroCall ( mcall) => mcall,
802- // attribute we failed expansion for earlier, this might be a derive invocation
803- // so try downmapping the token into the pseudo derive expansion
804- // see [hir_expand::builtin_attr_macro] for how the pseudo derive expansion works
805- ast:: Meta ( meta) => {
806- let attr = meta. parent_attr( ) ?;
807- let adt = attr. syntax( ) . parent( ) . and_then( ast:: Adt :: cast) ?;
808- let call_id = self . with_ctx( |ctx| {
809- let ( _, call_id, _) = ctx. attr_to_derive_macro_call(
810- token. with_value( & adt) ,
811- token. with_value( attr) ,
812- ) ?;
813- Some ( call_id)
814- } ) ?;
815- let file_id = call_id. as_file( ) ;
816- return process_expansion_for_token(
817- & mut stack,
818- file_id,
819- Some ( adt. into( ) ) ,
820- token. as_ref( ) ,
821- ) ;
822- } ,
823- _ => return None ,
824- }
825- } ;
789+ // Then check for token trees, that means we are either in a function-like macro or
790+ // secondary attribute inputs
791+ let tt = token. value . parent_ancestors ( ) . map_while ( ast:: TokenTree :: cast) . last ( ) ?;
792+ let parent = tt. syntax ( ) . parent ( ) ?;
826793
827- if tt. left_delimiter_token ( ) . map_or ( false , |it| it == token. value ) {
828- return None ;
829- }
830- if tt. right_delimiter_token ( ) . map_or ( false , |it| it == token. value ) {
831- return None ;
832- }
794+ if tt. left_delimiter_token ( ) . map_or ( false , |it| it == token. value ) {
795+ return None ;
796+ }
797+ if tt. right_delimiter_token ( ) . map_or ( false , |it| it == token. value ) {
798+ return None ;
799+ }
833800
801+ if let Some ( macro_call) = ast:: MacroCall :: cast ( parent. clone ( ) ) {
834802 let mcall = token. with_value ( macro_call) ;
835803 let file_id = match mcache. get ( & mcall) {
836804 Some ( & it) => it,
@@ -840,11 +808,75 @@ impl<'db> SemanticsImpl<'db> {
840808 it
841809 }
842810 } ;
843- return process_expansion_for_token ( & mut stack, file_id, None , token. as_ref ( ) ) ;
811+ process_expansion_for_token ( & mut stack, file_id, None , token. as_ref ( ) )
812+ } else if let Some ( meta) = ast:: Meta :: cast ( parent. clone ( ) ) {
813+ // attribute we failed expansion for earlier, this might be a derive invocation
814+ // or derive helper attribute
815+ let attr = meta. parent_attr ( ) ?;
816+
817+ let adt = if let Some ( adt) = attr. syntax ( ) . parent ( ) . and_then ( ast:: Adt :: cast) {
818+ // this might be a derive, or a derive helper on an ADT
819+ let derive_call = self . with_ctx ( |ctx| {
820+ // so try downmapping the token into the pseudo derive expansion
821+ // see [hir_expand::builtin_attr_macro] for how the pseudo derive expansion works
822+ ctx. attr_to_derive_macro_call (
823+ token. with_value ( & adt) ,
824+ token. with_value ( attr. clone ( ) ) ,
825+ )
826+ . map ( |( _, call_id, _) | call_id)
827+ } ) ;
828+
829+ match derive_call {
830+ Some ( call_id) => {
831+ // resolved to a derive
832+ let file_id = call_id. as_file ( ) ;
833+ return process_expansion_for_token (
834+ & mut stack,
835+ file_id,
836+ Some ( adt. into ( ) ) ,
837+ token. as_ref ( ) ,
838+ ) ;
839+ }
840+ None => Some ( adt) ,
841+ }
842+ } else {
843+ // Otherwise this could be a derive helper on a variant or field
844+ if let Some ( field) = attr. syntax ( ) . parent ( ) . and_then ( ast:: RecordField :: cast)
845+ {
846+ field. syntax ( ) . ancestors ( ) . take ( 4 ) . find_map ( ast:: Adt :: cast)
847+ } else if let Some ( field) =
848+ attr. syntax ( ) . parent ( ) . and_then ( ast:: TupleField :: cast)
849+ {
850+ field. syntax ( ) . ancestors ( ) . take ( 4 ) . find_map ( ast:: Adt :: cast)
851+ } else if let Some ( variant) =
852+ attr. syntax ( ) . parent ( ) . and_then ( ast:: Variant :: cast)
853+ {
854+ variant. syntax ( ) . ancestors ( ) . nth ( 2 ) . and_then ( ast:: Adt :: cast)
855+ } else {
856+ None
857+ }
858+ } ?;
859+
860+ // Not an attribute, nor a derive, so it's either a builtin or a derive helper
861+ // Try to resolve to a derive helper and downmap
862+ let attr_name = attr. path ( ) . and_then ( |it| it. as_single_name_ref ( ) ) ?. as_name ( ) ;
863+ let id = self . db . ast_id_map ( token. file_id ) . ast_id ( & adt) ;
864+ let helpers =
865+ def_map. derive_helpers_in_scope ( InFile :: new ( token. file_id , id) ) ?;
866+ let item = Some ( adt. into ( ) ) ;
867+ let mut res = None ;
868+ for ( _, derive) in helpers. iter ( ) . filter ( |( helper, _) | * helper == attr_name) {
869+ res = res. or ( process_expansion_for_token (
870+ & mut stack,
871+ derive. as_file ( ) ,
872+ item. clone ( ) ,
873+ token. as_ref ( ) ,
874+ ) ) ;
875+ }
876+ res
877+ } else {
878+ None
844879 }
845-
846- // outside of a macro invocation so this is a "final" token
847- None
848880 } ) ( )
849881 . is_none ( ) ;
850882
0 commit comments