@@ -380,6 +380,27 @@ impl<'db> SemanticsImpl<'db> {
380
380
self . with_ctx ( |ctx| ctx. has_derives ( adt) )
381
381
}
382
382
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
+
383
404
pub fn is_attr_macro_call ( & self , item : & ast:: Item ) -> bool {
384
405
let file_id = self . find_file ( item. syntax ( ) ) . file_id ;
385
406
let src = InFile :: new ( file_id, item. clone ( ) ) ;
@@ -409,6 +430,20 @@ impl<'db> SemanticsImpl<'db> {
409
430
)
410
431
}
411
432
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
+
412
447
/// Expand the macro call with a different item as the input, mapping the `token_to_map` down into the
413
448
/// expansion. `token_to_map` should be a token from the `speculative args` node.
414
449
pub fn speculative_expand_attr_macro (
@@ -826,99 +861,109 @@ impl<'db> SemanticsImpl<'db> {
826
861
827
862
// Then check for token trees, that means we are either in a function-like macro or
828
863
// 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 ;
848
872
}
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 ;
889
875
}
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
898
885
}
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
+ } ) )
903
898
}
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
918
966
}
919
- res
920
- } else {
921
- None
922
967
}
923
968
} ) ( )
924
969
. is_none ( ) ;
0 commit comments