diff options
Diffstat (limited to 'crates/ra_ide')
-rw-r--r-- | crates/ra_ide/src/completion/complete_qualified_path.rs | 273 |
1 files changed, 262 insertions, 11 deletions
diff --git a/crates/ra_ide/src/completion/complete_qualified_path.rs b/crates/ra_ide/src/completion/complete_qualified_path.rs index dd10f74e6..69e789a49 100644 --- a/crates/ra_ide/src/completion/complete_qualified_path.rs +++ b/crates/ra_ide/src/completion/complete_qualified_path.rs | |||
@@ -5,19 +5,30 @@ use ra_syntax::AstNode; | |||
5 | use test_utils::tested_by; | 5 | use test_utils::tested_by; |
6 | 6 | ||
7 | use crate::completion::{CompletionContext, Completions}; | 7 | use crate::completion::{CompletionContext, Completions}; |
8 | use rustc_hash::FxHashSet; | ||
8 | 9 | ||
9 | pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) { | 10 | pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) { |
10 | let path = match &ctx.path_prefix { | 11 | let path = match &ctx.path_prefix { |
11 | Some(path) => path.clone(), | 12 | Some(path) => path.clone(), |
12 | _ => return, | 13 | _ => return, |
13 | }; | 14 | }; |
14 | let def = match ctx.scope().resolve_hir_path(&path) { | 15 | let scope = ctx.scope(); |
15 | Some(PathResolution::Def(def)) => def, | 16 | let context_module = scope.module(); |
16 | _ => return, | 17 | |
18 | let res = if let Some(res) = scope.resolve_hir_path(&path) { | ||
19 | res | ||
20 | } else { | ||
21 | return; | ||
17 | }; | 22 | }; |
18 | let context_module = ctx.scope().module(); | 23 | |
19 | match def { | 24 | // Add associated types on type parameters and `Self`. |
20 | hir::ModuleDef::Module(module) => { | 25 | res.assoc_type_shorthand_candidates(ctx.db, |alias| { |
26 | acc.add_type_alias(ctx, alias); | ||
27 | None::<()> | ||
28 | }); | ||
29 | |||
30 | match res { | ||
31 | PathResolution::Def(hir::ModuleDef::Module(module)) => { | ||
21 | let module_scope = module.scope(ctx.db, context_module); | 32 | let module_scope = module.scope(ctx.db, context_module); |
22 | for (name, def) in module_scope { | 33 | for (name, def) in module_scope { |
23 | if ctx.use_item_syntax.is_some() { | 34 | if ctx.use_item_syntax.is_some() { |
@@ -35,7 +46,8 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
35 | acc.add_resolution(ctx, name.to_string(), &def); | 46 | acc.add_resolution(ctx, name.to_string(), &def); |
36 | } | 47 | } |
37 | } | 48 | } |
38 | hir::ModuleDef::Adt(_) | hir::ModuleDef::TypeAlias(_) => { | 49 | PathResolution::Def(def @ hir::ModuleDef::Adt(_)) |
50 | | PathResolution::Def(def @ hir::ModuleDef::TypeAlias(_)) => { | ||
39 | if let hir::ModuleDef::Adt(Adt::Enum(e)) = def { | 51 | if let hir::ModuleDef::Adt(Adt::Enum(e)) = def { |
40 | for variant in e.variants(ctx.db) { | 52 | for variant in e.variants(ctx.db) { |
41 | acc.add_enum_variant(ctx, variant, None); | 53 | acc.add_enum_variant(ctx, variant, None); |
@@ -46,8 +58,10 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
46 | hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db), | 58 | hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db), |
47 | _ => unreachable!(), | 59 | _ => unreachable!(), |
48 | }; | 60 | }; |
49 | // Iterate assoc types separately | 61 | |
50 | // FIXME: complete T::AssocType | 62 | // XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType. |
63 | // (where AssocType is defined on a trait, not an inherent impl) | ||
64 | |||
51 | let krate = ctx.krate; | 65 | let krate = ctx.krate; |
52 | if let Some(krate) = krate { | 66 | if let Some(krate) = krate { |
53 | let traits_in_scope = ctx.scope().traits_in_scope(); | 67 | let traits_in_scope = ctx.scope().traits_in_scope(); |
@@ -65,6 +79,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
65 | None::<()> | 79 | None::<()> |
66 | }); | 80 | }); |
67 | 81 | ||
82 | // Iterate assoc types separately | ||
68 | ty.iterate_impl_items(ctx.db, krate, |item| { | 83 | ty.iterate_impl_items(ctx.db, krate, |item| { |
69 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { | 84 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { |
70 | return None; | 85 | return None; |
@@ -77,7 +92,8 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
77 | }); | 92 | }); |
78 | } | 93 | } |
79 | } | 94 | } |
80 | hir::ModuleDef::Trait(t) => { | 95 | PathResolution::Def(hir::ModuleDef::Trait(t)) => { |
96 | // Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`. | ||
81 | for item in t.items(ctx.db) { | 97 | for item in t.items(ctx.db) { |
82 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { | 98 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { |
83 | continue; | 99 | continue; |
@@ -91,8 +107,38 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
91 | } | 107 | } |
92 | } | 108 | } |
93 | } | 109 | } |
110 | PathResolution::TypeParam(_) | PathResolution::SelfType(_) => { | ||
111 | if let Some(krate) = ctx.krate { | ||
112 | let ty = match res { | ||
113 | PathResolution::TypeParam(param) => param.ty(ctx.db), | ||
114 | PathResolution::SelfType(impl_def) => impl_def.target_ty(ctx.db), | ||
115 | _ => return, | ||
116 | }; | ||
117 | |||
118 | let traits_in_scope = ctx.scope().traits_in_scope(); | ||
119 | let mut seen = FxHashSet::default(); | ||
120 | ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { | ||
121 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { | ||
122 | return None; | ||
123 | } | ||
124 | |||
125 | // We might iterate candidates of a trait multiple times here, so deduplicate | ||
126 | // them. | ||
127 | if seen.insert(item) { | ||
128 | match item { | ||
129 | hir::AssocItem::Function(func) => { | ||
130 | acc.add_function(ctx, func, None); | ||
131 | } | ||
132 | hir::AssocItem::Const(ct) => acc.add_const(ctx, ct), | ||
133 | hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), | ||
134 | } | ||
135 | } | ||
136 | None::<()> | ||
137 | }); | ||
138 | } | ||
139 | } | ||
94 | _ => {} | 140 | _ => {} |
95 | }; | 141 | } |
96 | } | 142 | } |
97 | 143 | ||
98 | #[cfg(test)] | 144 | #[cfg(test)] |
@@ -844,6 +890,211 @@ mod tests { | |||
844 | } | 890 | } |
845 | 891 | ||
846 | #[test] | 892 | #[test] |
893 | fn completes_ty_param_assoc_ty() { | ||
894 | assert_debug_snapshot!( | ||
895 | do_reference_completion( | ||
896 | " | ||
897 | //- /lib.rs | ||
898 | trait Super { | ||
899 | type Ty; | ||
900 | const CONST: u8; | ||
901 | fn func() {} | ||
902 | fn method(&self) {} | ||
903 | } | ||
904 | |||
905 | trait Sub: Super { | ||
906 | type SubTy; | ||
907 | const C2: (); | ||
908 | fn subfunc() {} | ||
909 | fn submethod(&self) {} | ||
910 | } | ||
911 | |||
912 | fn foo<T: Sub>() { | ||
913 | T::<|> | ||
914 | } | ||
915 | " | ||
916 | ), | ||
917 | @r###" | ||
918 | [ | ||
919 | CompletionItem { | ||
920 | label: "C2", | ||
921 | source_range: 219..219, | ||
922 | delete: 219..219, | ||
923 | insert: "C2", | ||
924 | kind: Const, | ||
925 | detail: "const C2: ();", | ||
926 | }, | ||
927 | CompletionItem { | ||
928 | label: "CONST", | ||
929 | source_range: 219..219, | ||
930 | delete: 219..219, | ||
931 | insert: "CONST", | ||
932 | kind: Const, | ||
933 | detail: "const CONST: u8;", | ||
934 | }, | ||
935 | CompletionItem { | ||
936 | label: "SubTy", | ||
937 | source_range: 219..219, | ||
938 | delete: 219..219, | ||
939 | insert: "SubTy", | ||
940 | kind: TypeAlias, | ||
941 | detail: "type SubTy;", | ||
942 | }, | ||
943 | CompletionItem { | ||
944 | label: "Ty", | ||
945 | source_range: 219..219, | ||
946 | delete: 219..219, | ||
947 | insert: "Ty", | ||
948 | kind: TypeAlias, | ||
949 | detail: "type Ty;", | ||
950 | }, | ||
951 | CompletionItem { | ||
952 | label: "func()", | ||
953 | source_range: 219..219, | ||
954 | delete: 219..219, | ||
955 | insert: "func()$0", | ||
956 | kind: Function, | ||
957 | lookup: "func", | ||
958 | detail: "fn func()", | ||
959 | }, | ||
960 | CompletionItem { | ||
961 | label: "method()", | ||
962 | source_range: 219..219, | ||
963 | delete: 219..219, | ||
964 | insert: "method()$0", | ||
965 | kind: Method, | ||
966 | lookup: "method", | ||
967 | detail: "fn method(&self)", | ||
968 | }, | ||
969 | CompletionItem { | ||
970 | label: "subfunc()", | ||
971 | source_range: 219..219, | ||
972 | delete: 219..219, | ||
973 | insert: "subfunc()$0", | ||
974 | kind: Function, | ||
975 | lookup: "subfunc", | ||
976 | detail: "fn subfunc()", | ||
977 | }, | ||
978 | CompletionItem { | ||
979 | label: "submethod()", | ||
980 | source_range: 219..219, | ||
981 | delete: 219..219, | ||
982 | insert: "submethod()$0", | ||
983 | kind: Method, | ||
984 | lookup: "submethod", | ||
985 | detail: "fn submethod(&self)", | ||
986 | }, | ||
987 | ] | ||
988 | "### | ||
989 | ); | ||
990 | } | ||
991 | |||
992 | #[test] | ||
993 | fn completes_self_param_assoc_ty() { | ||
994 | assert_debug_snapshot!( | ||
995 | do_reference_completion( | ||
996 | " | ||
997 | //- /lib.rs | ||
998 | trait Super { | ||
999 | type Ty; | ||
1000 | const CONST: u8 = 0; | ||
1001 | fn func() {} | ||
1002 | fn method(&self) {} | ||
1003 | } | ||
1004 | |||
1005 | trait Sub: Super { | ||
1006 | type SubTy; | ||
1007 | const C2: () = (); | ||
1008 | fn subfunc() {} | ||
1009 | fn submethod(&self) {} | ||
1010 | } | ||
1011 | |||
1012 | struct Wrap<T>(T); | ||
1013 | impl<T> Super for Wrap<T> {} | ||
1014 | impl<T> Sub for Wrap<T> { | ||
1015 | fn subfunc() { | ||
1016 | // Should be able to assume `Self: Sub + Super` | ||
1017 | Self::<|> | ||
1018 | } | ||
1019 | } | ||
1020 | " | ||
1021 | ), | ||
1022 | @r###" | ||
1023 | [ | ||
1024 | CompletionItem { | ||
1025 | label: "C2", | ||
1026 | source_range: 365..365, | ||
1027 | delete: 365..365, | ||
1028 | insert: "C2", | ||
1029 | kind: Const, | ||
1030 | detail: "const C2: () = ();", | ||
1031 | }, | ||
1032 | CompletionItem { | ||
1033 | label: "CONST", | ||
1034 | source_range: 365..365, | ||
1035 | delete: 365..365, | ||
1036 | insert: "CONST", | ||
1037 | kind: Const, | ||
1038 | detail: "const CONST: u8 = 0;", | ||
1039 | }, | ||
1040 | CompletionItem { | ||
1041 | label: "SubTy", | ||
1042 | source_range: 365..365, | ||
1043 | delete: 365..365, | ||
1044 | insert: "SubTy", | ||
1045 | kind: TypeAlias, | ||
1046 | detail: "type SubTy;", | ||
1047 | }, | ||
1048 | CompletionItem { | ||
1049 | label: "Ty", | ||
1050 | source_range: 365..365, | ||
1051 | delete: 365..365, | ||
1052 | insert: "Ty", | ||
1053 | kind: TypeAlias, | ||
1054 | detail: "type Ty;", | ||
1055 | }, | ||
1056 | CompletionItem { | ||
1057 | label: "func()", | ||
1058 | source_range: 365..365, | ||
1059 | delete: 365..365, | ||
1060 | insert: "func()$0", | ||
1061 | kind: Function, | ||
1062 | lookup: "func", | ||
1063 | detail: "fn func()", | ||
1064 | }, | ||
1065 | CompletionItem { | ||
1066 | label: "method()", | ||
1067 | source_range: 365..365, | ||
1068 | delete: 365..365, | ||
1069 | insert: "method()$0", | ||
1070 | kind: Method, | ||
1071 | lookup: "method", | ||
1072 | detail: "fn method(&self)", | ||
1073 | }, | ||
1074 | CompletionItem { | ||
1075 | label: "subfunc()", | ||
1076 | source_range: 365..365, | ||
1077 | delete: 365..365, | ||
1078 | insert: "subfunc()$0", | ||
1079 | kind: Function, | ||
1080 | lookup: "subfunc", | ||
1081 | detail: "fn subfunc()", | ||
1082 | }, | ||
1083 | CompletionItem { | ||
1084 | label: "submethod()", | ||
1085 | source_range: 365..365, | ||
1086 | delete: 365..365, | ||
1087 | insert: "submethod()$0", | ||
1088 | kind: Method, | ||
1089 | lookup: "submethod", | ||
1090 | detail: "fn submethod(&self)", | ||
1091 | }, | ||
1092 | ] | ||
1093 | "### | ||
1094 | ); | ||
1095 | } | ||
1096 | |||
1097 | #[test] | ||
847 | fn completes_type_alias() { | 1098 | fn completes_type_alias() { |
848 | assert_debug_snapshot!( | 1099 | assert_debug_snapshot!( |
849 | do_reference_completion( | 1100 | do_reference_completion( |