2
2
3
3
use either:: Either ;
4
4
use hir:: { HasAttrs , HirDisplay , Semantics } ;
5
- use ide_db:: { active_parameter:: callable_for_token, base_db:: FilePosition } ;
5
+ use ide_db:: {
6
+ active_parameter:: { callable_for_token, generics_for_token} ,
7
+ base_db:: FilePosition ,
8
+ } ;
6
9
use stdx:: format_to;
7
10
use syntax:: { algo, AstNode , Direction , TextRange , TextSize } ;
8
11
@@ -27,8 +30,16 @@ impl CallInfo {
27
30
& self . parameters
28
31
}
29
32
30
- fn push_param ( & mut self , param : & str ) {
31
- if !self . signature . ends_with ( '(' ) {
33
+ fn push_call_param ( & mut self , param : & str ) {
34
+ self . push_param ( '(' , param) ;
35
+ }
36
+
37
+ fn push_generic_param ( & mut self , param : & str ) {
38
+ self . push_param ( '<' , param) ;
39
+ }
40
+
41
+ fn push_param ( & mut self , opening_delim : char , param : & str ) {
42
+ if !self . signature . ends_with ( opening_delim) {
32
43
self . signature . push_str ( ", " ) ;
33
44
}
34
45
let start = TextSize :: of ( & self . signature ) ;
@@ -51,8 +62,22 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
51
62
. and_then ( |tok| algo:: skip_trivia_token ( tok, Direction :: Prev ) ) ?;
52
63
let token = sema. descend_into_macros_single ( token) ;
53
64
54
- let ( callable, active_parameter) = callable_for_token ( & sema, token) ?;
65
+ if let Some ( ( callable, active_parameter) ) = callable_for_token ( & sema, token. clone ( ) ) {
66
+ return Some ( call_info_for_callable ( db, callable, active_parameter) ) ;
67
+ }
68
+
69
+ if let Some ( ( generic_def, active_parameter) ) = generics_for_token ( & sema, token. clone ( ) ) {
70
+ return call_info_for_generics ( db, generic_def, active_parameter) ;
71
+ }
72
+
73
+ None
74
+ }
55
75
76
+ fn call_info_for_callable (
77
+ db : & RootDatabase ,
78
+ callable : hir:: Callable ,
79
+ active_parameter : Option < usize > ,
80
+ ) -> CallInfo {
56
81
let mut res =
57
82
CallInfo { doc : None , signature : String :: new ( ) , parameters : vec ! [ ] , active_parameter } ;
58
83
@@ -92,7 +117,7 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
92
117
}
93
118
}
94
119
format_to ! ( buf, "{}" , ty. display( db) ) ;
95
- res. push_param ( & buf) ;
120
+ res. push_call_param ( & buf) ;
96
121
}
97
122
}
98
123
res. signature . push ( ')' ) ;
@@ -106,6 +131,75 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
106
131
}
107
132
hir:: CallableKind :: TupleStruct ( _) | hir:: CallableKind :: TupleEnumVariant ( _) => { }
108
133
}
134
+ res
135
+ }
136
+
137
+ fn call_info_for_generics (
138
+ db : & RootDatabase ,
139
+ mut generics_def : hir:: GenericDef ,
140
+ active_parameter : usize ,
141
+ ) -> Option < CallInfo > {
142
+ let mut res = CallInfo {
143
+ doc : None ,
144
+ signature : String :: new ( ) ,
145
+ parameters : vec ! [ ] ,
146
+ active_parameter : Some ( active_parameter) ,
147
+ } ;
148
+
149
+ match generics_def {
150
+ hir:: GenericDef :: Function ( it) => {
151
+ res. doc = it. docs ( db) . map ( |it| it. into ( ) ) ;
152
+ format_to ! ( res. signature, "fn {}" , it. name( db) ) ;
153
+ }
154
+ hir:: GenericDef :: Adt ( hir:: Adt :: Enum ( it) ) => {
155
+ res. doc = it. docs ( db) . map ( |it| it. into ( ) ) ;
156
+ format_to ! ( res. signature, "enum {}" , it. name( db) ) ;
157
+ }
158
+ hir:: GenericDef :: Adt ( hir:: Adt :: Struct ( it) ) => {
159
+ res. doc = it. docs ( db) . map ( |it| it. into ( ) ) ;
160
+ format_to ! ( res. signature, "struct {}" , it. name( db) ) ;
161
+ }
162
+ hir:: GenericDef :: Adt ( hir:: Adt :: Union ( it) ) => {
163
+ res. doc = it. docs ( db) . map ( |it| it. into ( ) ) ;
164
+ format_to ! ( res. signature, "union {}" , it. name( db) ) ;
165
+ }
166
+ hir:: GenericDef :: Trait ( it) => {
167
+ res. doc = it. docs ( db) . map ( |it| it. into ( ) ) ;
168
+ format_to ! ( res. signature, "trait {}" , it. name( db) ) ;
169
+ }
170
+ hir:: GenericDef :: TypeAlias ( it) => {
171
+ res. doc = it. docs ( db) . map ( |it| it. into ( ) ) ;
172
+ format_to ! ( res. signature, "type {}" , it. name( db) ) ;
173
+ }
174
+ hir:: GenericDef :: Variant ( it) => {
175
+ // In paths, generics of an enum can be specified *after* one of its variants.
176
+ // eg. `None::<u8>`
177
+ // We'll use the signature of the enum, but include the docs of the variant.
178
+ res. doc = it. docs ( db) . map ( |it| it. into ( ) ) ;
179
+ let it = it. parent_enum ( db) ;
180
+ format_to ! ( res. signature, "enum {}" , it. name( db) ) ;
181
+ generics_def = it. into ( ) ;
182
+ }
183
+ // These don't have generic args that can be specified
184
+ hir:: GenericDef :: Impl ( _) | hir:: GenericDef :: Const ( _) => return None ,
185
+ }
186
+
187
+ res. signature . push ( '<' ) ;
188
+ let params = generics_def. params ( db) ;
189
+ let mut buf = String :: new ( ) ;
190
+ for param in params {
191
+ if let hir:: GenericParam :: TypeParam ( ty) = param {
192
+ if ty. is_implicit ( db) {
193
+ continue ;
194
+ }
195
+ }
196
+
197
+ buf. clear ( ) ;
198
+ format_to ! ( buf, "{}" , param. display( db) ) ;
199
+ res. push_generic_param ( & buf) ;
200
+ }
201
+ res. signature . push ( '>' ) ;
202
+
109
203
Some ( res)
110
204
}
111
205
@@ -128,7 +222,14 @@ mod tests {
128
222
}
129
223
130
224
fn check ( ra_fixture : & str , expect : Expect ) {
131
- let ( db, position) = position ( ra_fixture) ;
225
+ // Implicitly add `Sized` to avoid noisy `T: ?Sized` in the results.
226
+ let fixture = format ! (
227
+ r#"
228
+ #[lang = "sized"] trait Sized {{}}
229
+ {ra_fixture}
230
+ "#
231
+ ) ;
232
+ let ( db, position) = position ( & fixture) ;
132
233
let call_info = crate :: call_info:: call_info ( & db, position) ;
133
234
let actual = match call_info {
134
235
Some ( call_info) => {
@@ -676,4 +777,138 @@ fn main() {
676
777
"# ] ] ,
677
778
)
678
779
}
780
+
781
+ #[ test]
782
+ fn test_generics_simple ( ) {
783
+ check (
784
+ r#"
785
+ /// Option docs.
786
+ enum Option<T> {
787
+ Some(T),
788
+ None,
789
+ }
790
+
791
+ fn f() {
792
+ let opt: Option<$0
793
+ }
794
+ "# ,
795
+ expect ! [ [ r#"
796
+ Option docs.
797
+ ------
798
+ enum Option<T>
799
+ (<T>)
800
+ "# ] ] ,
801
+ ) ;
802
+ }
803
+
804
+ #[ test]
805
+ fn test_generics_on_variant ( ) {
806
+ check (
807
+ r#"
808
+ /// Option docs.
809
+ enum Option<T> {
810
+ /// Some docs.
811
+ Some(T),
812
+ /// None docs.
813
+ None,
814
+ }
815
+
816
+ use Option::*;
817
+
818
+ fn f() {
819
+ None::<$0
820
+ }
821
+ "# ,
822
+ expect ! [ [ r#"
823
+ None docs.
824
+ ------
825
+ enum Option<T>
826
+ (<T>)
827
+ "# ] ] ,
828
+ ) ;
829
+ }
830
+
831
+ #[ test]
832
+ fn test_lots_of_generics ( ) {
833
+ check (
834
+ r#"
835
+ trait Tr<T> {}
836
+
837
+ struct S<T>(T);
838
+
839
+ impl<T> S<T> {
840
+ fn f<G, H>(g: G, h: impl Tr<G>) where G: Tr<()> {}
841
+ }
842
+
843
+ fn f() {
844
+ S::<u8>::f::<(), $0
845
+ }
846
+ "# ,
847
+ expect ! [ [ r#"
848
+ fn f<G: Tr<()>, H>
849
+ (G: Tr<()>, <H>)
850
+ "# ] ] ,
851
+ ) ;
852
+ }
853
+
854
+ #[ test]
855
+ fn test_generics_in_trait_ufcs ( ) {
856
+ check (
857
+ r#"
858
+ trait Tr {
859
+ fn f<T: Tr, U>() {}
860
+ }
861
+
862
+ struct S;
863
+
864
+ impl Tr for S {}
865
+
866
+ fn f() {
867
+ <S as Tr>::f::<$0
868
+ }
869
+ "# ,
870
+ expect ! [ [ r#"
871
+ fn f<T: Tr, U>
872
+ (<T: Tr>, U)
873
+ "# ] ] ,
874
+ ) ;
875
+ }
876
+
877
+ #[ test]
878
+ fn test_generics_in_method_call ( ) {
879
+ check (
880
+ r#"
881
+ struct S;
882
+
883
+ impl S {
884
+ fn f<T>(&self) {}
885
+ }
886
+
887
+ fn f() {
888
+ S.f::<$0
889
+ }
890
+ "# ,
891
+ expect ! [ [ r#"
892
+ fn f<T>
893
+ (<T>)
894
+ "# ] ] ,
895
+ ) ;
896
+ }
897
+
898
+ #[ test]
899
+ fn test_generic_kinds ( ) {
900
+ check (
901
+ r#"
902
+ fn callee<'a, const A: (), T, const C: u8>() {}
903
+
904
+ fn f() {
905
+ callee::<'static, $0
906
+ }
907
+ "# ,
908
+ expect ! [ [ r#"
909
+ fn callee<'a, const A: (), T, const C: u8>
910
+ ('a, <const A: ()>, T, const C: u8)
911
+ "# ] ] ,
912
+ ) ;
913
+ }
679
914
}
0 commit comments