@@ -380,6 +380,27 @@ impl<'db> SemanticsImpl<'db> {
380380 self . with_ctx ( |ctx| ctx. has_derives ( adt) )
381381 }
382382
383+ pub fn derive_helper ( & self , attr : & ast:: Attr ) -> Option < Vec < ( Macro , MacroFileId ) > > {
384+ let adt = attr. syntax ( ) . ancestors ( ) . find_map ( ast:: Item :: cast) . and_then ( |it| match it {
385+ ast:: Item :: Struct ( it) => Some ( ast:: Adt :: Struct ( it) ) ,
386+ ast:: Item :: Enum ( it) => Some ( ast:: Adt :: Enum ( it) ) ,
387+ ast:: Item :: Union ( it) => Some ( ast:: Adt :: Union ( it) ) ,
388+ _ => None ,
389+ } ) ?;
390+ let attr_name = attr. path ( ) . and_then ( |it| it. as_single_name_ref ( ) ) ?. as_name ( ) ;
391+ let sa = self . analyze_no_infer ( adt. syntax ( ) ) ?;
392+ let id = self . db . ast_id_map ( sa. file_id ) . ast_id ( & adt) ;
393+ let res: Vec < _ > = sa
394+ . resolver
395+ . def_map ( )
396+ . derive_helpers_in_scope ( InFile :: new ( sa. file_id , id) ) ?
397+ . iter ( )
398+ . filter ( |& ( name, _, _) | * name == attr_name)
399+ . map ( |& ( _, macro_, call) | ( macro_. into ( ) , call. as_macro_file ( ) ) )
400+ . collect ( ) ;
401+ res. is_empty ( ) . not ( ) . then_some ( res)
402+ }
403+
383404 pub fn is_attr_macro_call ( & self , item : & ast:: Item ) -> bool {
384405 let file_id = self . find_file ( item. syntax ( ) ) . file_id ;
385406 let src = InFile :: new ( file_id, item. clone ( ) ) ;
@@ -409,6 +430,20 @@ impl<'db> SemanticsImpl<'db> {
409430 )
410431 }
411432
433+ pub fn speculative_expand_raw (
434+ & self ,
435+ macro_file : MacroFileId ,
436+ speculative_args : & SyntaxNode ,
437+ token_to_map : SyntaxToken ,
438+ ) -> Option < ( SyntaxNode , SyntaxToken ) > {
439+ hir_expand:: db:: expand_speculative (
440+ self . db . upcast ( ) ,
441+ macro_file. macro_call_id ,
442+ speculative_args,
443+ token_to_map,
444+ )
445+ }
446+
412447 /// Expand the macro call with a different item as the input, mapping the `token_to_map` down into the
413448 /// expansion. `token_to_map` should be a token from the `speculative args` node.
414449 pub fn speculative_expand_attr_macro (
@@ -826,99 +861,109 @@ impl<'db> SemanticsImpl<'db> {
826861
827862 // Then check for token trees, that means we are either in a function-like macro or
828863 // secondary attribute inputs
829- let tt = token. parent_ancestors ( ) . map_while ( ast:: TokenTree :: cast) . last ( ) ?;
830- let parent = tt. syntax ( ) . parent ( ) ?;
831-
832- if tt. left_delimiter_token ( ) . map_or ( false , |it| it == token) {
833- return None ;
834- }
835- if tt. right_delimiter_token ( ) . map_or ( false , |it| it == token) {
836- return None ;
837- }
838-
839- if let Some ( macro_call) = ast:: MacroCall :: cast ( parent. clone ( ) ) {
840- let mcall: hir_expand:: files:: InFileWrapper < HirFileId , ast:: MacroCall > =
841- InFile :: new ( file_id, macro_call) ;
842- let file_id = match mcache. get ( & mcall) {
843- Some ( & it) => it,
844- None => {
845- let it = sa. expand ( self . db , mcall. as_ref ( ) ) ?;
846- mcache. insert ( mcall, it) ;
847- it
864+ let tt = token
865+ . parent_ancestors ( )
866+ . map_while ( Either :: < ast:: TokenTree , ast:: Meta > :: cast)
867+ . last ( ) ?;
868+ match tt {
869+ Either :: Left ( tt) => {
870+ if tt. left_delimiter_token ( ) . map_or ( false , |it| it == token) {
871+ return None ;
848872 }
849- } ;
850- let text_range = tt. syntax ( ) . text_range ( ) ;
851- // remove any other token in this macro input, all their mappings are the
852- // same as this one
853- tokens. retain ( |t| !text_range. contains_range ( t. text_range ( ) ) ) ;
854-
855- process_expansion_for_token ( & mut stack, file_id) . or ( file_id
856- . eager_arg ( self . db . upcast ( ) )
857- . and_then ( |arg| {
858- // also descend into eager expansions
859- process_expansion_for_token ( & mut stack, arg. as_macro_file ( ) )
860- } ) )
861- } else if let Some ( meta) = ast:: Meta :: cast ( parent) {
862- // attribute we failed expansion for earlier, this might be a derive invocation
863- // or derive helper attribute
864- let attr = meta. parent_attr ( ) ?;
865- let adt = if let Some ( adt) = attr. syntax ( ) . parent ( ) . and_then ( ast:: Adt :: cast)
866- {
867- // this might be a derive on an ADT
868- let derive_call = self . with_ctx ( |ctx| {
869- // so try downmapping the token into the pseudo derive expansion
870- // see [hir_expand::builtin_attr_macro] for how the pseudo derive expansion works
871- ctx. attr_to_derive_macro_call (
872- InFile :: new ( file_id, & adt) ,
873- InFile :: new ( file_id, attr. clone ( ) ) ,
874- )
875- . map ( |( _, call_id, _) | call_id)
876- } ) ;
877-
878- match derive_call {
879- Some ( call_id) => {
880- // resolved to a derive
881- let file_id = call_id. as_macro_file ( ) ;
882- let text_range = attr. syntax ( ) . text_range ( ) ;
883- // remove any other token in this macro input, all their mappings are the
884- // same as this
885- tokens. retain ( |t| !text_range. contains_range ( t. text_range ( ) ) ) ;
886- return process_expansion_for_token ( & mut stack, file_id) ;
887- }
888- None => Some ( adt) ,
873+ if tt. right_delimiter_token ( ) . map_or ( false , |it| it == token) {
874+ return None ;
889875 }
890- } else {
891- // Otherwise this could be a derive helper on a variant or field
892- attr. syntax ( ) . ancestors ( ) . find_map ( ast:: Item :: cast) . and_then ( |it| {
893- match it {
894- ast:: Item :: Struct ( it) => Some ( ast:: Adt :: Struct ( it) ) ,
895- ast:: Item :: Enum ( it) => Some ( ast:: Adt :: Enum ( it) ) ,
896- ast:: Item :: Union ( it) => Some ( ast:: Adt :: Union ( it) ) ,
897- _ => None ,
876+ let macro_call = tt. syntax ( ) . parent ( ) . and_then ( ast:: MacroCall :: cast) ?;
877+ let mcall: hir_expand:: files:: InFileWrapper < HirFileId , ast:: MacroCall > =
878+ InFile :: new ( file_id, macro_call) ;
879+ let file_id = match mcache. get ( & mcall) {
880+ Some ( & it) => it,
881+ None => {
882+ let it = sa. expand ( self . db , mcall. as_ref ( ) ) ?;
883+ mcache. insert ( mcall, it) ;
884+ it
898885 }
899- } )
900- } ?;
901- if !self . with_ctx ( |ctx| ctx. has_derives ( InFile :: new ( file_id, & adt) ) ) {
902- return None ;
886+ } ;
887+ let text_range = tt. syntax ( ) . text_range ( ) ;
888+ // remove any other token in this macro input, all their mappings are the
889+ // same as this one
890+ tokens. retain ( |t| !text_range. contains_range ( t. text_range ( ) ) ) ;
891+
892+ process_expansion_for_token ( & mut stack, file_id) . or ( file_id
893+ . eager_arg ( self . db . upcast ( ) )
894+ . and_then ( |arg| {
895+ // also descend into eager expansions
896+ process_expansion_for_token ( & mut stack, arg. as_macro_file ( ) )
897+ } ) )
903898 }
904- // Not an attribute, nor a derive, so it's either a builtin or a derive helper
905- // Try to resolve to a derive helper and downmap
906- let attr_name =
907- attr. path ( ) . and_then ( |it| it. as_single_name_ref ( ) ) ?. as_name ( ) ;
908- let id = self . db . ast_id_map ( file_id) . ast_id ( & adt) ;
909- let helpers = def_map. derive_helpers_in_scope ( InFile :: new ( file_id, id) ) ?;
910- let mut res = None ;
911- for ( .., derive) in
912- helpers. iter ( ) . filter ( |( helper, ..) | * helper == attr_name)
913- {
914- res = res. or ( process_expansion_for_token (
915- & mut stack,
916- derive. as_macro_file ( ) ,
917- ) ) ;
899+ Either :: Right ( meta) => {
900+ // attribute we failed expansion for earlier, this might be a derive invocation
901+ // or derive helper attribute
902+ let attr = meta. parent_attr ( ) ?;
903+ let adt = match attr. syntax ( ) . parent ( ) . and_then ( ast:: Adt :: cast) {
904+ Some ( adt) => {
905+ // this might be a derive on an ADT
906+ let derive_call = self . with_ctx ( |ctx| {
907+ // so try downmapping the token into the pseudo derive expansion
908+ // see [hir_expand::builtin_attr_macro] for how the pseudo derive expansion works
909+ ctx. attr_to_derive_macro_call (
910+ InFile :: new ( file_id, & adt) ,
911+ InFile :: new ( file_id, attr. clone ( ) ) ,
912+ )
913+ . map ( |( _, call_id, _) | call_id)
914+ } ) ;
915+
916+ match derive_call {
917+ Some ( call_id) => {
918+ // resolved to a derive
919+ let file_id = call_id. as_macro_file ( ) ;
920+ let text_range = attr. syntax ( ) . text_range ( ) ;
921+ // remove any other token in this macro input, all their mappings are the
922+ // same as this
923+ tokens. retain ( |t| {
924+ !text_range. contains_range ( t. text_range ( ) )
925+ } ) ;
926+ return process_expansion_for_token (
927+ & mut stack, file_id,
928+ ) ;
929+ }
930+ None => Some ( adt) ,
931+ }
932+ }
933+ None => {
934+ // Otherwise this could be a derive helper on a variant or field
935+ attr. syntax ( ) . ancestors ( ) . find_map ( ast:: Item :: cast) . and_then (
936+ |it| match it {
937+ ast:: Item :: Struct ( it) => Some ( ast:: Adt :: Struct ( it) ) ,
938+ ast:: Item :: Enum ( it) => Some ( ast:: Adt :: Enum ( it) ) ,
939+ ast:: Item :: Union ( it) => Some ( ast:: Adt :: Union ( it) ) ,
940+ _ => None ,
941+ } ,
942+ )
943+ }
944+ } ?;
945+ if !self . with_ctx ( |ctx| ctx. has_derives ( InFile :: new ( file_id, & adt) ) ) {
946+ return None ;
947+ }
948+ let attr_name =
949+ attr. path ( ) . and_then ( |it| it. as_single_name_ref ( ) ) ?. as_name ( ) ;
950+ // Not an attribute, nor a derive, so it's either a builtin or a derive helper
951+ // Try to resolve to a derive helper and downmap
952+ let id = self . db . ast_id_map ( file_id) . ast_id ( & adt) ;
953+ let helpers =
954+ def_map. derive_helpers_in_scope ( InFile :: new ( file_id, id) ) ?;
955+
956+ let mut res = None ;
957+ for ( .., derive) in
958+ helpers. iter ( ) . filter ( |( helper, ..) | * helper == attr_name)
959+ {
960+ res = res. or ( process_expansion_for_token (
961+ & mut stack,
962+ derive. as_macro_file ( ) ,
963+ ) ) ;
964+ }
965+ res
918966 }
919- res
920- } else {
921- None
922967 }
923968 } ) ( )
924969 . is_none ( ) ;
0 commit comments