diff options
37 files changed, 1376 insertions, 329 deletions
diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs index 2e63cb46e..91e0ee619 100644 --- a/crates/ra_db/src/lib.rs +++ b/crates/ra_db/src/lib.rs | |||
@@ -11,8 +11,8 @@ use ra_syntax::{ast, Parse, SourceFile, TextRange, TextSize}; | |||
11 | pub use crate::{ | 11 | pub use crate::{ |
12 | cancellation::Canceled, | 12 | cancellation::Canceled, |
13 | input::{ | 13 | input::{ |
14 | CrateGraph, CrateId, CrateName, Dependency, Edition, Env, ExternSource, ExternSourceId, | 14 | CrateData, CrateGraph, CrateId, CrateName, Dependency, Edition, Env, ExternSource, |
15 | FileId, ProcMacroId, SourceRoot, SourceRootId, | 15 | ExternSourceId, FileId, ProcMacroId, SourceRoot, SourceRootId, |
16 | }, | 16 | }, |
17 | }; | 17 | }; |
18 | pub use relative_path::{RelativePath, RelativePathBuf}; | 18 | pub use relative_path::{RelativePath, RelativePathBuf}; |
@@ -89,14 +89,13 @@ pub const DEFAULT_LRU_CAP: usize = 128; | |||
89 | pub trait FileLoader { | 89 | pub trait FileLoader { |
90 | /// Text of the file. | 90 | /// Text of the file. |
91 | fn file_text(&self, file_id: FileId) -> Arc<String>; | 91 | fn file_text(&self, file_id: FileId) -> Arc<String>; |
92 | /// Note that we intentionally accept a `&str` and not a `&Path` here. This | ||
93 | /// method exists to handle `#[path = "/some/path.rs"] mod foo;` and such, | ||
94 | /// so the input is guaranteed to be utf-8 string. We might introduce | ||
95 | /// `struct StrPath(str)` for clarity some day, but it's a bit messy, so we | ||
96 | /// get by with a `&str` for the time being. | ||
92 | fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId>; | 97 | fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId>; |
93 | fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>>; | 98 | fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>>; |
94 | |||
95 | fn resolve_extern_path( | ||
96 | &self, | ||
97 | extern_id: ExternSourceId, | ||
98 | relative_path: &RelativePath, | ||
99 | ) -> Option<FileId>; | ||
100 | } | 99 | } |
101 | 100 | ||
102 | /// Database which stores all significant input facts: source code and project | 101 | /// Database which stores all significant input facts: source code and project |
@@ -154,34 +153,30 @@ impl<T: SourceDatabaseExt> FileLoader for FileLoaderDelegate<&'_ T> { | |||
154 | fn file_text(&self, file_id: FileId) -> Arc<String> { | 153 | fn file_text(&self, file_id: FileId) -> Arc<String> { |
155 | SourceDatabaseExt::file_text(self.0, file_id) | 154 | SourceDatabaseExt::file_text(self.0, file_id) |
156 | } | 155 | } |
157 | /// Note that we intentionally accept a `&str` and not a `&Path` here. This | ||
158 | /// method exists to handle `#[path = "/some/path.rs"] mod foo;` and such, | ||
159 | /// so the input is guaranteed to be utf-8 string. We might introduce | ||
160 | /// `struct StrPath(str)` for clarity some day, but it's a bit messy, so we | ||
161 | /// get by with a `&str` for the time being. | ||
162 | fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> { | 156 | fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> { |
163 | let rel_path = { | 157 | // FIXME: this *somehow* should be platform agnostic... |
164 | let mut rel_path = self.0.file_relative_path(anchor); | 158 | if std::path::Path::new(path).is_absolute() { |
165 | assert!(rel_path.pop()); | 159 | let krate = *self.relevant_crates(anchor).get(0)?; |
166 | rel_path.push(path); | 160 | let (extern_source_id, relative_file) = |
167 | rel_path.normalize() | 161 | self.0.crate_graph()[krate].extern_source.extern_path(path)?; |
168 | }; | 162 | |
169 | let source_root = self.0.file_source_root(anchor); | 163 | let source_root = self.0.source_root(SourceRootId(extern_source_id.0)); |
170 | let source_root = self.0.source_root(source_root); | 164 | source_root.file_by_relative_path(&relative_file) |
171 | source_root.file_by_relative_path(&rel_path) | 165 | } else { |
166 | let rel_path = { | ||
167 | let mut rel_path = self.0.file_relative_path(anchor); | ||
168 | assert!(rel_path.pop()); | ||
169 | rel_path.push(path); | ||
170 | rel_path.normalize() | ||
171 | }; | ||
172 | let source_root = self.0.file_source_root(anchor); | ||
173 | let source_root = self.0.source_root(source_root); | ||
174 | source_root.file_by_relative_path(&rel_path) | ||
175 | } | ||
172 | } | 176 | } |
173 | 177 | ||
174 | fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { | 178 | fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { |
175 | let source_root = self.0.file_source_root(file_id); | 179 | let source_root = self.0.file_source_root(file_id); |
176 | self.0.source_root_crates(source_root) | 180 | self.0.source_root_crates(source_root) |
177 | } | 181 | } |
178 | |||
179 | fn resolve_extern_path( | ||
180 | &self, | ||
181 | extern_id: ExternSourceId, | ||
182 | relative_path: &RelativePath, | ||
183 | ) -> Option<FileId> { | ||
184 | let source_root = self.0.source_root(SourceRootId(extern_id.0)); | ||
185 | source_root.file_by_relative_path(&relative_path) | ||
186 | } | ||
187 | } | 182 | } |
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index ec931b34f..b6b665de1 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs | |||
@@ -3,11 +3,11 @@ | |||
3 | pub use hir_def::db::{ | 3 | pub use hir_def::db::{ |
4 | AttrsQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery, CrateDefMapQueryQuery, | 4 | AttrsQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery, CrateDefMapQueryQuery, |
5 | CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, DocumentationQuery, EnumDataQuery, | 5 | CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, DocumentationQuery, EnumDataQuery, |
6 | ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, InternConstQuery, | 6 | ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, ImportMapQuery, |
7 | InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery, InternImplQuery, | 7 | InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery, |
8 | InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery, InternUnionQuery, | 8 | InternImplQuery, InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery, |
9 | LangItemQuery, ModuleLangItemsQuery, RawItemsQuery, StaticDataQuery, StructDataQuery, | 9 | InternUnionQuery, LangItemQuery, ModuleLangItemsQuery, RawItemsQuery, StaticDataQuery, |
10 | TraitDataQuery, TypeAliasDataQuery, UnionDataQuery, | 10 | StructDataQuery, TraitDataQuery, TypeAliasDataQuery, UnionDataQuery, |
11 | }; | 11 | }; |
12 | pub use hir_expand::db::{ | 12 | pub use hir_expand::db::{ |
13 | AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery, | 13 | AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery, |
@@ -18,8 +18,8 @@ pub use hir_ty::db::{ | |||
18 | GenericDefaultsQuery, GenericPredicatesForParamQuery, GenericPredicatesQuery, HirDatabase, | 18 | GenericDefaultsQuery, GenericPredicatesForParamQuery, GenericPredicatesQuery, HirDatabase, |
19 | HirDatabaseStorage, ImplDatumQuery, ImplSelfTyQuery, ImplTraitQuery, ImplsForTraitQuery, | 19 | HirDatabaseStorage, ImplDatumQuery, ImplSelfTyQuery, ImplTraitQuery, ImplsForTraitQuery, |
20 | ImplsInCrateQuery, InferQueryQuery, InternAssocTyValueQuery, InternChalkImplQuery, | 20 | ImplsInCrateQuery, InferQueryQuery, InternAssocTyValueQuery, InternChalkImplQuery, |
21 | InternTypeCtorQuery, InternTypeParamIdQuery, StructDatumQuery, TraitDatumQuery, | 21 | InternTypeCtorQuery, InternTypeParamIdQuery, ReturnTypeImplTraitsQuery, StructDatumQuery, |
22 | TraitSolveQuery, TyQuery, ValueTyQuery, | 22 | TraitDatumQuery, TraitSolveQuery, TyQuery, ValueTyQuery, |
23 | }; | 23 | }; |
24 | 24 | ||
25 | #[test] | 25 | #[test] |
diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs index 945a0025e..10cc26480 100644 --- a/crates/ra_hir_def/src/db.rs +++ b/crates/ra_hir_def/src/db.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! Defines database & queries for name resolution. | 1 | //! Defines database & queries for name resolution. |
2 | use std::sync::Arc; | 2 | use std::sync::Arc; |
3 | 3 | ||
4 | use hir_expand::{db::AstDatabase, name::Name, HirFileId}; | 4 | use hir_expand::{db::AstDatabase, HirFileId}; |
5 | use ra_db::{salsa, CrateId, SourceDatabase, Upcast}; | 5 | use ra_db::{salsa, CrateId, SourceDatabase, Upcast}; |
6 | use ra_prof::profile; | 6 | use ra_prof::profile; |
7 | use ra_syntax::SmolStr; | 7 | use ra_syntax::SmolStr; |
@@ -12,13 +12,10 @@ use crate::{ | |||
12 | body::{scope::ExprScopes, Body, BodySourceMap}, | 12 | body::{scope::ExprScopes, Body, BodySourceMap}, |
13 | data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData}, | 13 | data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData}, |
14 | docs::Documentation, | 14 | docs::Documentation, |
15 | find_path, | ||
16 | generics::GenericParams, | 15 | generics::GenericParams, |
17 | item_scope::ItemInNs, | 16 | import_map::ImportMap, |
18 | lang_item::{LangItemTarget, LangItems}, | 17 | lang_item::{LangItemTarget, LangItems}, |
19 | nameres::{raw::RawItems, CrateDefMap}, | 18 | nameres::{raw::RawItems, CrateDefMap}, |
20 | path::ModPath, | ||
21 | visibility::Visibility, | ||
22 | AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, | 19 | AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, |
23 | GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId, | 20 | GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId, |
24 | TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, | 21 | TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, |
@@ -113,15 +110,8 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { | |||
113 | #[salsa::invoke(Documentation::documentation_query)] | 110 | #[salsa::invoke(Documentation::documentation_query)] |
114 | fn documentation(&self, def: AttrDefId) -> Option<Documentation>; | 111 | fn documentation(&self, def: AttrDefId) -> Option<Documentation>; |
115 | 112 | ||
116 | #[salsa::invoke(find_path::importable_locations_of_query)] | 113 | #[salsa::invoke(ImportMap::import_map_query)] |
117 | fn importable_locations_of( | 114 | fn import_map(&self, krate: CrateId) -> Arc<ImportMap>; |
118 | &self, | ||
119 | item: ItemInNs, | ||
120 | krate: CrateId, | ||
121 | ) -> Arc<[(ModuleId, Name, Visibility)]>; | ||
122 | |||
123 | #[salsa::invoke(find_path::find_path_inner_query)] | ||
124 | fn find_path_inner(&self, item: ItemInNs, from: ModuleId, max_len: usize) -> Option<ModPath>; | ||
125 | } | 115 | } |
126 | 116 | ||
127 | fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> { | 117 | fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> { |
diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs index 4db798473..a7f59e028 100644 --- a/crates/ra_hir_def/src/find_path.rs +++ b/crates/ra_hir_def/src/find_path.rs | |||
@@ -1,9 +1,8 @@ | |||
1 | //! An algorithm to find a path to refer to a certain item. | 1 | //! An algorithm to find a path to refer to a certain item. |
2 | 2 | ||
3 | use std::sync::Arc; | ||
4 | |||
5 | use hir_expand::name::{known, AsName, Name}; | 3 | use hir_expand::name::{known, AsName, Name}; |
6 | use ra_prof::profile; | 4 | use ra_prof::profile; |
5 | use rustc_hash::FxHashSet; | ||
7 | use test_utils::mark; | 6 | use test_utils::mark; |
8 | 7 | ||
9 | use crate::{ | 8 | use crate::{ |
@@ -11,7 +10,7 @@ use crate::{ | |||
11 | item_scope::ItemInNs, | 10 | item_scope::ItemInNs, |
12 | path::{ModPath, PathKind}, | 11 | path::{ModPath, PathKind}, |
13 | visibility::Visibility, | 12 | visibility::Visibility, |
14 | CrateId, ModuleDefId, ModuleId, | 13 | ModuleDefId, ModuleId, |
15 | }; | 14 | }; |
16 | 15 | ||
17 | // FIXME: handle local items | 16 | // FIXME: handle local items |
@@ -20,7 +19,7 @@ use crate::{ | |||
20 | /// *from where* you're referring to the item, hence the `from` parameter. | 19 | /// *from where* you're referring to the item, hence the `from` parameter. |
21 | pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { | 20 | pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { |
22 | let _p = profile("find_path"); | 21 | let _p = profile("find_path"); |
23 | db.find_path_inner(item, from, MAX_PATH_LEN) | 22 | find_path_inner(db, item, from, MAX_PATH_LEN) |
24 | } | 23 | } |
25 | 24 | ||
26 | const MAX_PATH_LEN: usize = 15; | 25 | const MAX_PATH_LEN: usize = 15; |
@@ -36,20 +35,9 @@ impl ModPath { | |||
36 | let first_segment = self.segments.first(); | 35 | let first_segment = self.segments.first(); |
37 | first_segment == Some(&known::alloc) || first_segment == Some(&known::core) | 36 | first_segment == Some(&known::alloc) || first_segment == Some(&known::core) |
38 | } | 37 | } |
39 | |||
40 | fn len(&self) -> usize { | ||
41 | self.segments.len() | ||
42 | + match self.kind { | ||
43 | PathKind::Plain => 0, | ||
44 | PathKind::Super(i) => i as usize, | ||
45 | PathKind::Crate => 1, | ||
46 | PathKind::Abs => 0, | ||
47 | PathKind::DollarCrate(_) => 1, | ||
48 | } | ||
49 | } | ||
50 | } | 38 | } |
51 | 39 | ||
52 | pub(crate) fn find_path_inner_query( | 40 | fn find_path_inner( |
53 | db: &dyn DefDatabase, | 41 | db: &dyn DefDatabase, |
54 | item: ItemInNs, | 42 | item: ItemInNs, |
55 | from: ModuleId, | 43 | from: ModuleId, |
@@ -133,31 +121,61 @@ pub(crate) fn find_path_inner_query( | |||
133 | } | 121 | } |
134 | 122 | ||
135 | // - otherwise, look for modules containing (reexporting) it and import it from one of those | 123 | // - otherwise, look for modules containing (reexporting) it and import it from one of those |
124 | |||
136 | let crate_root = ModuleId { local_id: def_map.root, krate: from.krate }; | 125 | let crate_root = ModuleId { local_id: def_map.root, krate: from.krate }; |
137 | let crate_attrs = db.attrs(crate_root.into()); | 126 | let crate_attrs = db.attrs(crate_root.into()); |
138 | let prefer_no_std = crate_attrs.by_key("no_std").exists(); | 127 | let prefer_no_std = crate_attrs.by_key("no_std").exists(); |
139 | let importable_locations = find_importable_locations(db, item, from); | ||
140 | let mut best_path = None; | 128 | let mut best_path = None; |
141 | let mut best_path_len = max_len; | 129 | let mut best_path_len = max_len; |
142 | for (module_id, name) in importable_locations { | ||
143 | let mut path = match db.find_path_inner( | ||
144 | ItemInNs::Types(ModuleDefId::ModuleId(module_id)), | ||
145 | from, | ||
146 | best_path_len - 1, | ||
147 | ) { | ||
148 | None => continue, | ||
149 | Some(path) => path, | ||
150 | }; | ||
151 | path.segments.push(name); | ||
152 | 130 | ||
153 | let new_path = if let Some(best_path) = best_path { | 131 | if item.krate(db) == Some(from.krate) { |
154 | select_best_path(best_path, path, prefer_no_std) | 132 | // Item was defined in the same crate that wants to import it. It cannot be found in any |
155 | } else { | 133 | // dependency in this case. |
156 | path | 134 | |
157 | }; | 135 | let local_imports = find_local_import_locations(db, item, from); |
158 | best_path_len = new_path.len(); | 136 | for (module_id, name) in local_imports { |
159 | best_path = Some(new_path); | 137 | if let Some(mut path) = find_path_inner( |
138 | db, | ||
139 | ItemInNs::Types(ModuleDefId::ModuleId(module_id)), | ||
140 | from, | ||
141 | best_path_len - 1, | ||
142 | ) { | ||
143 | path.segments.push(name); | ||
144 | |||
145 | let new_path = if let Some(best_path) = best_path { | ||
146 | select_best_path(best_path, path, prefer_no_std) | ||
147 | } else { | ||
148 | path | ||
149 | }; | ||
150 | best_path_len = new_path.len(); | ||
151 | best_path = Some(new_path); | ||
152 | } | ||
153 | } | ||
154 | } else { | ||
155 | // Item was defined in some upstream crate. This means that it must be exported from one, | ||
156 | // too (unless we can't name it at all). It could *also* be (re)exported by the same crate | ||
157 | // that wants to import it here, but we always prefer to use the external path here. | ||
158 | |||
159 | let crate_graph = db.crate_graph(); | ||
160 | let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| { | ||
161 | let import_map = db.import_map(dep.crate_id); | ||
162 | import_map.path_of(item).map(|modpath| { | ||
163 | let mut modpath = modpath.clone(); | ||
164 | modpath.segments.insert(0, dep.as_name()); | ||
165 | modpath | ||
166 | }) | ||
167 | }); | ||
168 | |||
169 | for path in extern_paths { | ||
170 | let new_path = if let Some(best_path) = best_path { | ||
171 | select_best_path(best_path, path, prefer_no_std) | ||
172 | } else { | ||
173 | path | ||
174 | }; | ||
175 | best_path = Some(new_path); | ||
176 | } | ||
160 | } | 177 | } |
178 | |||
161 | best_path | 179 | best_path |
162 | } | 180 | } |
163 | 181 | ||
@@ -185,69 +203,86 @@ fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) - | |||
185 | } | 203 | } |
186 | } | 204 | } |
187 | 205 | ||
188 | fn find_importable_locations( | 206 | /// Finds locations in `from.krate` from which `item` can be imported by `from`. |
207 | fn find_local_import_locations( | ||
189 | db: &dyn DefDatabase, | 208 | db: &dyn DefDatabase, |
190 | item: ItemInNs, | 209 | item: ItemInNs, |
191 | from: ModuleId, | 210 | from: ModuleId, |
192 | ) -> Vec<(ModuleId, Name)> { | 211 | ) -> Vec<(ModuleId, Name)> { |
193 | let crate_graph = db.crate_graph(); | 212 | let _p = profile("find_local_import_locations"); |
194 | let mut result = Vec::new(); | 213 | |
195 | // We only look in the crate from which we are importing, and the direct | 214 | // `from` can import anything below `from` with visibility of at least `from`, and anything |
196 | // dependencies. We cannot refer to names from transitive dependencies | 215 | // above `from` with any visibility. That means we do not need to descend into private siblings |
197 | // directly (only through reexports in direct dependencies). | 216 | // of `from` (and similar). |
198 | for krate in Some(from.krate) | 217 | |
199 | .into_iter() | 218 | let def_map = db.crate_def_map(from.krate); |
200 | .chain(crate_graph[from.krate].dependencies.iter().map(|dep| dep.crate_id)) | 219 | |
201 | { | 220 | // Compute the initial worklist. We start with all direct child modules of `from` as well as all |
202 | result.extend( | 221 | // of its (recursive) parent modules. |
203 | db.importable_locations_of(item, krate) | 222 | let data = &def_map.modules[from.local_id]; |
204 | .iter() | 223 | let mut worklist = data |
205 | .filter(|(_, _, vis)| vis.is_visible_from(db, from)) | 224 | .children |
206 | .map(|(m, n, _)| (*m, n.clone())), | 225 | .values() |
207 | ); | 226 | .map(|child| ModuleId { krate: from.krate, local_id: *child }) |
208 | } | 227 | .collect::<Vec<_>>(); |
209 | result | 228 | let mut parent = data.parent; |
210 | } | 229 | while let Some(p) = parent { |
230 | worklist.push(ModuleId { krate: from.krate, local_id: p }); | ||
231 | parent = def_map.modules[p].parent; | ||
232 | } | ||
233 | |||
234 | let mut seen: FxHashSet<_> = FxHashSet::default(); | ||
235 | |||
236 | let mut locations = Vec::new(); | ||
237 | while let Some(module) = worklist.pop() { | ||
238 | if !seen.insert(module) { | ||
239 | continue; // already processed this module | ||
240 | } | ||
241 | |||
242 | let ext_def_map; | ||
243 | let data = if module.krate == from.krate { | ||
244 | &def_map[module.local_id] | ||
245 | } else { | ||
246 | // The crate might reexport a module defined in another crate. | ||
247 | ext_def_map = db.crate_def_map(module.krate); | ||
248 | &ext_def_map[module.local_id] | ||
249 | }; | ||
211 | 250 | ||
212 | /// Collects all locations from which we might import the item in a particular | ||
213 | /// crate. These include the original definition of the item, and any | ||
214 | /// non-private `use`s. | ||
215 | /// | ||
216 | /// Note that the crate doesn't need to be the one in which the item is defined; | ||
217 | /// it might be re-exported in other crates. | ||
218 | pub(crate) fn importable_locations_of_query( | ||
219 | db: &dyn DefDatabase, | ||
220 | item: ItemInNs, | ||
221 | krate: CrateId, | ||
222 | ) -> Arc<[(ModuleId, Name, Visibility)]> { | ||
223 | let _p = profile("importable_locations_of_query"); | ||
224 | let def_map = db.crate_def_map(krate); | ||
225 | let mut result = Vec::new(); | ||
226 | for (local_id, data) in def_map.modules.iter() { | ||
227 | if let Some((name, vis)) = data.scope.name_of(item) { | 251 | if let Some((name, vis)) = data.scope.name_of(item) { |
228 | let is_private = if let Visibility::Module(private_to) = vis { | 252 | if vis.is_visible_from(db, from) { |
229 | private_to.local_id == local_id | 253 | let is_private = if let Visibility::Module(private_to) = vis { |
230 | } else { | 254 | private_to.local_id == module.local_id |
231 | false | 255 | } else { |
232 | }; | 256 | false |
233 | let is_original_def = if let Some(module_def_id) = item.as_module_def_id() { | 257 | }; |
234 | data.scope.declarations().any(|it| it == module_def_id) | 258 | let is_original_def = if let Some(module_def_id) = item.as_module_def_id() { |
235 | } else { | 259 | data.scope.declarations().any(|it| it == module_def_id) |
236 | false | 260 | } else { |
237 | }; | 261 | false |
238 | if is_private && !is_original_def { | 262 | }; |
263 | |||
239 | // Ignore private imports. these could be used if we are | 264 | // Ignore private imports. these could be used if we are |
240 | // in a submodule of this module, but that's usually not | 265 | // in a submodule of this module, but that's usually not |
241 | // what the user wants; and if this module can import | 266 | // what the user wants; and if this module can import |
242 | // the item and we're a submodule of it, so can we. | 267 | // the item and we're a submodule of it, so can we. |
243 | // Also this keeps the cached data smaller. | 268 | // Also this keeps the cached data smaller. |
244 | continue; | 269 | if !is_private || is_original_def { |
270 | locations.push((module, name.clone())); | ||
271 | } | ||
272 | } | ||
273 | } | ||
274 | |||
275 | // Descend into all modules visible from `from`. | ||
276 | for (_, per_ns) in data.scope.entries() { | ||
277 | if let Some((ModuleDefId::ModuleId(module), vis)) = per_ns.take_types_vis() { | ||
278 | if vis.is_visible_from(db, from) { | ||
279 | worklist.push(module); | ||
280 | } | ||
245 | } | 281 | } |
246 | result.push((ModuleId { krate, local_id }, name.clone(), vis)); | ||
247 | } | 282 | } |
248 | } | 283 | } |
249 | 284 | ||
250 | Arc::from(result) | 285 | locations |
251 | } | 286 | } |
252 | 287 | ||
253 | #[cfg(test)] | 288 | #[cfg(test)] |
@@ -385,6 +420,7 @@ mod tests { | |||
385 | 420 | ||
386 | #[test] | 421 | #[test] |
387 | fn different_crate_renamed() { | 422 | fn different_crate_renamed() { |
423 | // Even if a local path exists, if the item is defined externally, prefer an external path. | ||
388 | let code = r#" | 424 | let code = r#" |
389 | //- /main.rs crate:main deps:std | 425 | //- /main.rs crate:main deps:std |
390 | extern crate std as std_renamed; | 426 | extern crate std as std_renamed; |
@@ -392,7 +428,7 @@ mod tests { | |||
392 | //- /std.rs crate:std | 428 | //- /std.rs crate:std |
393 | pub struct S; | 429 | pub struct S; |
394 | "#; | 430 | "#; |
395 | check_found_path(code, "std_renamed::S"); | 431 | check_found_path(code, "std::S"); |
396 | } | 432 | } |
397 | 433 | ||
398 | #[test] | 434 | #[test] |
diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs new file mode 100644 index 000000000..4284a0a91 --- /dev/null +++ b/crates/ra_hir_def/src/import_map.rs | |||
@@ -0,0 +1,331 @@ | |||
1 | //! A map of all publicly exported items in a crate. | ||
2 | |||
3 | use std::{collections::hash_map::Entry, fmt, sync::Arc}; | ||
4 | |||
5 | use ra_db::CrateId; | ||
6 | use rustc_hash::FxHashMap; | ||
7 | |||
8 | use crate::{ | ||
9 | db::DefDatabase, | ||
10 | item_scope::ItemInNs, | ||
11 | path::{ModPath, PathKind}, | ||
12 | visibility::Visibility, | ||
13 | ModuleDefId, ModuleId, | ||
14 | }; | ||
15 | |||
16 | /// A map from publicly exported items to the path needed to import/name them from a downstream | ||
17 | /// crate. | ||
18 | /// | ||
19 | /// Reexports of items are taken into account, ie. if something is exported under multiple | ||
20 | /// names, the one with the shortest import path will be used. | ||
21 | /// | ||
22 | /// Note that all paths are relative to the containing crate's root, so the crate name still needs | ||
23 | /// to be prepended to the `ModPath` before the path is valid. | ||
24 | #[derive(Eq, PartialEq)] | ||
25 | pub struct ImportMap { | ||
26 | map: FxHashMap<ItemInNs, ModPath>, | ||
27 | } | ||
28 | |||
29 | impl ImportMap { | ||
30 | pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> { | ||
31 | let _p = ra_prof::profile("import_map_query"); | ||
32 | let def_map = db.crate_def_map(krate); | ||
33 | let mut import_map = FxHashMap::with_capacity_and_hasher(64, Default::default()); | ||
34 | |||
35 | // We look only into modules that are public(ly reexported), starting with the crate root. | ||
36 | let empty = ModPath { kind: PathKind::Plain, segments: vec![] }; | ||
37 | let root = ModuleId { krate, local_id: def_map.root }; | ||
38 | let mut worklist = vec![(root, empty)]; | ||
39 | while let Some((module, mod_path)) = worklist.pop() { | ||
40 | let ext_def_map; | ||
41 | let mod_data = if module.krate == krate { | ||
42 | &def_map[module.local_id] | ||
43 | } else { | ||
44 | // The crate might reexport a module defined in another crate. | ||
45 | ext_def_map = db.crate_def_map(module.krate); | ||
46 | &ext_def_map[module.local_id] | ||
47 | }; | ||
48 | |||
49 | let visible_items = mod_data.scope.entries().filter_map(|(name, per_ns)| { | ||
50 | let per_ns = per_ns.filter_visibility(|vis| vis == Visibility::Public); | ||
51 | if per_ns.is_none() { | ||
52 | None | ||
53 | } else { | ||
54 | Some((name, per_ns)) | ||
55 | } | ||
56 | }); | ||
57 | |||
58 | for (name, per_ns) in visible_items { | ||
59 | let mk_path = || { | ||
60 | let mut path = mod_path.clone(); | ||
61 | path.segments.push(name.clone()); | ||
62 | path | ||
63 | }; | ||
64 | |||
65 | for item in per_ns.iter_items() { | ||
66 | let path = mk_path(); | ||
67 | match import_map.entry(item) { | ||
68 | Entry::Vacant(entry) => { | ||
69 | entry.insert(path); | ||
70 | } | ||
71 | Entry::Occupied(mut entry) => { | ||
72 | // If the new path is shorter, prefer that one. | ||
73 | if path.len() < entry.get().len() { | ||
74 | *entry.get_mut() = path; | ||
75 | } else { | ||
76 | continue; | ||
77 | } | ||
78 | } | ||
79 | } | ||
80 | |||
81 | // If we've just added a path to a module, descend into it. We might traverse | ||
82 | // modules multiple times, but only if the new path to it is shorter than the | ||
83 | // first (else we `continue` above). | ||
84 | if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() { | ||
85 | worklist.push((mod_id, mk_path())); | ||
86 | } | ||
87 | } | ||
88 | } | ||
89 | } | ||
90 | |||
91 | Arc::new(Self { map: import_map }) | ||
92 | } | ||
93 | |||
94 | /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root. | ||
95 | pub fn path_of(&self, item: ItemInNs) -> Option<&ModPath> { | ||
96 | self.map.get(&item) | ||
97 | } | ||
98 | } | ||
99 | |||
100 | impl fmt::Debug for ImportMap { | ||
101 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
102 | let mut importable_paths: Vec<_> = self | ||
103 | .map | ||
104 | .iter() | ||
105 | .map(|(item, modpath)| { | ||
106 | let ns = match item { | ||
107 | ItemInNs::Types(_) => "t", | ||
108 | ItemInNs::Values(_) => "v", | ||
109 | ItemInNs::Macros(_) => "m", | ||
110 | }; | ||
111 | format!("- {} ({})", modpath, ns) | ||
112 | }) | ||
113 | .collect(); | ||
114 | |||
115 | importable_paths.sort(); | ||
116 | f.write_str(&importable_paths.join("\n")) | ||
117 | } | ||
118 | } | ||
119 | |||
120 | #[cfg(test)] | ||
121 | mod tests { | ||
122 | use super::*; | ||
123 | use crate::test_db::TestDB; | ||
124 | use insta::assert_snapshot; | ||
125 | use ra_db::fixture::WithFixture; | ||
126 | use ra_db::SourceDatabase; | ||
127 | |||
128 | fn import_map(ra_fixture: &str) -> String { | ||
129 | let db = TestDB::with_files(ra_fixture); | ||
130 | let crate_graph = db.crate_graph(); | ||
131 | |||
132 | let import_maps: Vec<_> = crate_graph | ||
133 | .iter() | ||
134 | .filter_map(|krate| { | ||
135 | let cdata = &crate_graph[krate]; | ||
136 | let name = cdata.display_name.as_ref()?; | ||
137 | |||
138 | let map = db.import_map(krate); | ||
139 | |||
140 | Some(format!("{}:\n{:?}", name, map)) | ||
141 | }) | ||
142 | .collect(); | ||
143 | |||
144 | import_maps.join("\n") | ||
145 | } | ||
146 | |||
147 | #[test] | ||
148 | fn smoke() { | ||
149 | let map = import_map( | ||
150 | r" | ||
151 | //- /main.rs crate:main deps:lib | ||
152 | |||
153 | mod private { | ||
154 | pub use lib::Pub; | ||
155 | pub struct InPrivateModule; | ||
156 | } | ||
157 | |||
158 | pub mod publ1 { | ||
159 | use lib::Pub; | ||
160 | } | ||
161 | |||
162 | pub mod real_pub { | ||
163 | pub use lib::Pub; | ||
164 | } | ||
165 | pub mod real_pu2 { // same path length as above | ||
166 | pub use lib::Pub; | ||
167 | } | ||
168 | |||
169 | //- /lib.rs crate:lib | ||
170 | pub struct Pub {} | ||
171 | pub struct Pub2; // t + v | ||
172 | struct Priv; | ||
173 | ", | ||
174 | ); | ||
175 | |||
176 | assert_snapshot!(map, @r###" | ||
177 | main: | ||
178 | - publ1 (t) | ||
179 | - real_pu2 (t) | ||
180 | - real_pub (t) | ||
181 | - real_pub::Pub (t) | ||
182 | lib: | ||
183 | - Pub (t) | ||
184 | - Pub2 (t) | ||
185 | - Pub2 (v) | ||
186 | "###); | ||
187 | } | ||
188 | |||
189 | #[test] | ||
190 | fn prefers_shortest_path() { | ||
191 | let map = import_map( | ||
192 | r" | ||
193 | //- /main.rs crate:main | ||
194 | |||
195 | pub mod sub { | ||
196 | pub mod subsub { | ||
197 | pub struct Def {} | ||
198 | } | ||
199 | |||
200 | pub use super::sub::subsub::Def; | ||
201 | } | ||
202 | ", | ||
203 | ); | ||
204 | |||
205 | assert_snapshot!(map, @r###" | ||
206 | main: | ||
207 | - sub (t) | ||
208 | - sub::Def (t) | ||
209 | - sub::subsub (t) | ||
210 | "###); | ||
211 | } | ||
212 | |||
213 | #[test] | ||
214 | fn type_reexport_cross_crate() { | ||
215 | // Reexports need to be visible from a crate, even if the original crate exports the item | ||
216 | // at a shorter path. | ||
217 | let map = import_map( | ||
218 | r" | ||
219 | //- /main.rs crate:main deps:lib | ||
220 | pub mod m { | ||
221 | pub use lib::S; | ||
222 | } | ||
223 | //- /lib.rs crate:lib | ||
224 | pub struct S; | ||
225 | ", | ||
226 | ); | ||
227 | |||
228 | assert_snapshot!(map, @r###" | ||
229 | main: | ||
230 | - m (t) | ||
231 | - m::S (t) | ||
232 | - m::S (v) | ||
233 | lib: | ||
234 | - S (t) | ||
235 | - S (v) | ||
236 | "###); | ||
237 | } | ||
238 | |||
239 | #[test] | ||
240 | fn macro_reexport() { | ||
241 | let map = import_map( | ||
242 | r" | ||
243 | //- /main.rs crate:main deps:lib | ||
244 | pub mod m { | ||
245 | pub use lib::pub_macro; | ||
246 | } | ||
247 | //- /lib.rs crate:lib | ||
248 | #[macro_export] | ||
249 | macro_rules! pub_macro { | ||
250 | () => {}; | ||
251 | } | ||
252 | ", | ||
253 | ); | ||
254 | |||
255 | assert_snapshot!(map, @r###" | ||
256 | main: | ||
257 | - m (t) | ||
258 | - m::pub_macro (m) | ||
259 | lib: | ||
260 | - pub_macro (m) | ||
261 | "###); | ||
262 | } | ||
263 | |||
264 | #[test] | ||
265 | fn module_reexport() { | ||
266 | // Reexporting modules from a dependency adds all contents to the import map. | ||
267 | let map = import_map( | ||
268 | r" | ||
269 | //- /main.rs crate:main deps:lib | ||
270 | pub use lib::module as reexported_module; | ||
271 | //- /lib.rs crate:lib | ||
272 | pub mod module { | ||
273 | pub struct S; | ||
274 | } | ||
275 | ", | ||
276 | ); | ||
277 | |||
278 | assert_snapshot!(map, @r###" | ||
279 | main: | ||
280 | - reexported_module (t) | ||
281 | - reexported_module::S (t) | ||
282 | - reexported_module::S (v) | ||
283 | lib: | ||
284 | - module (t) | ||
285 | - module::S (t) | ||
286 | - module::S (v) | ||
287 | "###); | ||
288 | } | ||
289 | |||
290 | #[test] | ||
291 | fn cyclic_module_reexport() { | ||
292 | // A cyclic reexport does not hang. | ||
293 | let map = import_map( | ||
294 | r" | ||
295 | //- /lib.rs crate:lib | ||
296 | pub mod module { | ||
297 | pub struct S; | ||
298 | pub use super::sub::*; | ||
299 | } | ||
300 | |||
301 | pub mod sub { | ||
302 | pub use super::module; | ||
303 | } | ||
304 | ", | ||
305 | ); | ||
306 | |||
307 | assert_snapshot!(map, @r###" | ||
308 | lib: | ||
309 | - module (t) | ||
310 | - module::S (t) | ||
311 | - module::S (v) | ||
312 | - sub (t) | ||
313 | "###); | ||
314 | } | ||
315 | |||
316 | #[test] | ||
317 | fn private_macro() { | ||
318 | let map = import_map( | ||
319 | r" | ||
320 | //- /lib.rs crate:lib | ||
321 | macro_rules! private_macro { | ||
322 | () => {}; | ||
323 | } | ||
324 | ", | ||
325 | ); | ||
326 | |||
327 | assert_snapshot!(map, @r###" | ||
328 | lib: | ||
329 | "###); | ||
330 | } | ||
331 | } | ||
diff --git a/crates/ra_hir_def/src/item_scope.rs b/crates/ra_hir_def/src/item_scope.rs index fc15948ad..b03ba939a 100644 --- a/crates/ra_hir_def/src/item_scope.rs +++ b/crates/ra_hir_def/src/item_scope.rs | |||
@@ -3,11 +3,12 @@ | |||
3 | 3 | ||
4 | use hir_expand::name::Name; | 4 | use hir_expand::name::Name; |
5 | use once_cell::sync::Lazy; | 5 | use once_cell::sync::Lazy; |
6 | use ra_db::CrateId; | ||
6 | use rustc_hash::FxHashMap; | 7 | use rustc_hash::FxHashMap; |
7 | 8 | ||
8 | use crate::{ | 9 | use crate::{ |
9 | per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ImplId, MacroDefId, ModuleDefId, | 10 | db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId, |
10 | TraitId, | 11 | Lookup, MacroDefId, ModuleDefId, TraitId, |
11 | }; | 12 | }; |
12 | 13 | ||
13 | #[derive(Debug, Default, PartialEq, Eq)] | 14 | #[derive(Debug, Default, PartialEq, Eq)] |
@@ -203,4 +204,22 @@ impl ItemInNs { | |||
203 | ItemInNs::Macros(_) => None, | 204 | ItemInNs::Macros(_) => None, |
204 | } | 205 | } |
205 | } | 206 | } |
207 | |||
208 | /// Returns the crate defining this item (or `None` if `self` is built-in). | ||
209 | pub fn krate(&self, db: &dyn DefDatabase) -> Option<CrateId> { | ||
210 | Some(match self { | ||
211 | ItemInNs::Types(did) | ItemInNs::Values(did) => match did { | ||
212 | ModuleDefId::ModuleId(id) => id.krate, | ||
213 | ModuleDefId::FunctionId(id) => id.lookup(db).module(db).krate, | ||
214 | ModuleDefId::AdtId(id) => id.module(db).krate, | ||
215 | ModuleDefId::EnumVariantId(id) => id.parent.lookup(db).container.module(db).krate, | ||
216 | ModuleDefId::ConstId(id) => id.lookup(db).container.module(db).krate, | ||
217 | ModuleDefId::StaticId(id) => id.lookup(db).container.module(db).krate, | ||
218 | ModuleDefId::TraitId(id) => id.lookup(db).container.module(db).krate, | ||
219 | ModuleDefId::TypeAliasId(id) => id.lookup(db).module(db).krate, | ||
220 | ModuleDefId::BuiltinType(_) => return None, | ||
221 | }, | ||
222 | ItemInNs::Macros(id) => return id.krate, | ||
223 | }) | ||
224 | } | ||
206 | } | 225 | } |
diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs index 5325a2760..de490fcc5 100644 --- a/crates/ra_hir_def/src/lib.rs +++ b/crates/ra_hir_def/src/lib.rs | |||
@@ -43,6 +43,7 @@ pub mod child_by_source; | |||
43 | 43 | ||
44 | pub mod visibility; | 44 | pub mod visibility; |
45 | pub mod find_path; | 45 | pub mod find_path; |
46 | pub mod import_map; | ||
46 | 47 | ||
47 | #[cfg(test)] | 48 | #[cfg(test)] |
48 | mod test_db; | 49 | mod test_db; |
diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs index 4512448e0..bfa921de2 100644 --- a/crates/ra_hir_def/src/path.rs +++ b/crates/ra_hir_def/src/path.rs | |||
@@ -76,6 +76,19 @@ impl ModPath { | |||
76 | } | 76 | } |
77 | } | 77 | } |
78 | 78 | ||
79 | /// Returns the number of segments in the path (counting special segments like `$crate` and | ||
80 | /// `super`). | ||
81 | pub fn len(&self) -> usize { | ||
82 | self.segments.len() | ||
83 | + match self.kind { | ||
84 | PathKind::Plain => 0, | ||
85 | PathKind::Super(i) => i as usize, | ||
86 | PathKind::Crate => 1, | ||
87 | PathKind::Abs => 0, | ||
88 | PathKind::DollarCrate(_) => 1, | ||
89 | } | ||
90 | } | ||
91 | |||
79 | pub fn is_ident(&self) -> bool { | 92 | pub fn is_ident(&self) -> bool { |
80 | self.kind == PathKind::Plain && self.segments.len() == 1 | 93 | self.kind == PathKind::Plain && self.segments.len() == 1 |
81 | } | 94 | } |
diff --git a/crates/ra_hir_def/src/per_ns.rs b/crates/ra_hir_def/src/per_ns.rs index 6e435c8c1..74665c588 100644 --- a/crates/ra_hir_def/src/per_ns.rs +++ b/crates/ra_hir_def/src/per_ns.rs | |||
@@ -5,7 +5,7 @@ | |||
5 | 5 | ||
6 | use hir_expand::MacroDefId; | 6 | use hir_expand::MacroDefId; |
7 | 7 | ||
8 | use crate::{visibility::Visibility, ModuleDefId}; | 8 | use crate::{item_scope::ItemInNs, visibility::Visibility, ModuleDefId}; |
9 | 9 | ||
10 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | 10 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] |
11 | pub struct PerNs { | 11 | pub struct PerNs { |
@@ -84,4 +84,12 @@ impl PerNs { | |||
84 | macros: self.macros.or(other.macros), | 84 | macros: self.macros.or(other.macros), |
85 | } | 85 | } |
86 | } | 86 | } |
87 | |||
88 | pub fn iter_items(self) -> impl Iterator<Item = ItemInNs> { | ||
89 | self.types | ||
90 | .map(|it| ItemInNs::Types(it.0)) | ||
91 | .into_iter() | ||
92 | .chain(self.values.map(|it| ItemInNs::Values(it.0)).into_iter()) | ||
93 | .chain(self.macros.map(|it| ItemInNs::Macros(it.0)).into_iter()) | ||
94 | } | ||
87 | } | 95 | } |
diff --git a/crates/ra_hir_def/src/test_db.rs b/crates/ra_hir_def/src/test_db.rs index e7a5182f0..bcfa66ac9 100644 --- a/crates/ra_hir_def/src/test_db.rs +++ b/crates/ra_hir_def/src/test_db.rs | |||
@@ -6,9 +6,7 @@ use std::{ | |||
6 | }; | 6 | }; |
7 | 7 | ||
8 | use hir_expand::db::AstDatabase; | 8 | use hir_expand::db::AstDatabase; |
9 | use ra_db::{ | 9 | use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast}; |
10 | salsa, CrateId, ExternSourceId, FileId, FileLoader, FileLoaderDelegate, RelativePath, Upcast, | ||
11 | }; | ||
12 | 10 | ||
13 | use crate::db::DefDatabase; | 11 | use crate::db::DefDatabase; |
14 | 12 | ||
@@ -64,14 +62,6 @@ impl FileLoader for TestDB { | |||
64 | fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { | 62 | fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { |
65 | FileLoaderDelegate(self).relevant_crates(file_id) | 63 | FileLoaderDelegate(self).relevant_crates(file_id) |
66 | } | 64 | } |
67 | |||
68 | fn resolve_extern_path( | ||
69 | &self, | ||
70 | extern_id: ExternSourceId, | ||
71 | relative_path: &RelativePath, | ||
72 | ) -> Option<FileId> { | ||
73 | FileLoaderDelegate(self).resolve_extern_path(extern_id, relative_path) | ||
74 | } | ||
75 | } | 65 | } |
76 | 66 | ||
77 | impl TestDB { | 67 | impl TestDB { |
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs index eec5fb8eb..7579546d2 100644 --- a/crates/ra_hir_expand/src/builtin_macro.rs +++ b/crates/ra_hir_expand/src/builtin_macro.rs | |||
@@ -295,19 +295,13 @@ fn concat_expand( | |||
295 | 295 | ||
296 | fn relative_file(db: &dyn AstDatabase, call_id: MacroCallId, path: &str) -> Option<FileId> { | 296 | fn relative_file(db: &dyn AstDatabase, call_id: MacroCallId, path: &str) -> Option<FileId> { |
297 | let call_site = call_id.as_file().original_file(db); | 297 | let call_site = call_id.as_file().original_file(db); |
298 | 298 | let res = db.resolve_path(call_site, path)?; | |
299 | // Handle trivial case | 299 | // Prevent include itself |
300 | if let Some(res) = db.resolve_path(call_site, path) { | 300 | if res == call_site { |
301 | // Prevent include itself | 301 | None |
302 | return if res == call_site { None } else { Some(res) }; | 302 | } else { |
303 | Some(res) | ||
303 | } | 304 | } |
304 | |||
305 | // Extern paths ? | ||
306 | let krate = *db.relevant_crates(call_site).get(0)?; | ||
307 | let (extern_source_id, relative_file) = | ||
308 | db.crate_graph()[krate].extern_source.extern_path(path)?; | ||
309 | |||
310 | db.resolve_extern_path(extern_source_id, &relative_file) | ||
311 | } | 305 | } |
312 | 306 | ||
313 | fn parse_string(tt: &tt::Subtree) -> Result<String, mbe::ExpandError> { | 307 | fn parse_string(tt: &tt::Subtree) -> Result<String, mbe::ExpandError> { |
diff --git a/crates/ra_hir_expand/src/test_db.rs b/crates/ra_hir_expand/src/test_db.rs index 765a2f6d1..fdf225f55 100644 --- a/crates/ra_hir_expand/src/test_db.rs +++ b/crates/ra_hir_expand/src/test_db.rs | |||
@@ -5,7 +5,7 @@ use std::{ | |||
5 | sync::{Arc, Mutex}, | 5 | sync::{Arc, Mutex}, |
6 | }; | 6 | }; |
7 | 7 | ||
8 | use ra_db::{salsa, CrateId, ExternSourceId, FileId, FileLoader, FileLoaderDelegate, RelativePath}; | 8 | use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate}; |
9 | 9 | ||
10 | #[salsa::database( | 10 | #[salsa::database( |
11 | ra_db::SourceDatabaseExtStorage, | 11 | ra_db::SourceDatabaseExtStorage, |
@@ -47,11 +47,4 @@ impl FileLoader for TestDB { | |||
47 | fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { | 47 | fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { |
48 | FileLoaderDelegate(self).relevant_crates(file_id) | 48 | FileLoaderDelegate(self).relevant_crates(file_id) |
49 | } | 49 | } |
50 | fn resolve_extern_path( | ||
51 | &self, | ||
52 | anchor: ExternSourceId, | ||
53 | relative_path: &RelativePath, | ||
54 | ) -> Option<FileId> { | ||
55 | FileLoaderDelegate(self).resolve_extern_path(anchor, relative_path) | ||
56 | } | ||
57 | } | 50 | } |
diff --git a/crates/ra_hir_ty/src/db.rs b/crates/ra_hir_ty/src/db.rs index 0a8bb24ac..bf71d38d6 100644 --- a/crates/ra_hir_ty/src/db.rs +++ b/crates/ra_hir_ty/src/db.rs | |||
@@ -3,8 +3,8 @@ | |||
3 | use std::sync::Arc; | 3 | use std::sync::Arc; |
4 | 4 | ||
5 | use hir_def::{ | 5 | use hir_def::{ |
6 | db::DefDatabase, DefWithBodyId, GenericDefId, ImplId, LocalFieldId, TraitId, TypeParamId, | 6 | db::DefDatabase, DefWithBodyId, FunctionId, GenericDefId, ImplId, LocalFieldId, TraitId, |
7 | VariantId, | 7 | TypeParamId, VariantId, |
8 | }; | 8 | }; |
9 | use ra_arena::map::ArenaMap; | 9 | use ra_arena::map::ArenaMap; |
10 | use ra_db::{impl_intern_key, salsa, CrateId, Upcast}; | 10 | use ra_db::{impl_intern_key, salsa, CrateId, Upcast}; |
@@ -13,8 +13,8 @@ use ra_prof::profile; | |||
13 | use crate::{ | 13 | use crate::{ |
14 | method_resolution::{CrateImplDefs, TyFingerprint}, | 14 | method_resolution::{CrateImplDefs, TyFingerprint}, |
15 | traits::{chalk, AssocTyValue, Impl}, | 15 | traits::{chalk, AssocTyValue, Impl}, |
16 | Binders, CallableDef, GenericPredicate, InferenceResult, PolyFnSig, Substs, TraitRef, Ty, | 16 | Binders, CallableDef, GenericPredicate, InferenceResult, OpaqueTyId, PolyFnSig, |
17 | TyDefId, TypeCtor, ValueTyDefId, | 17 | ReturnTypeImplTraits, Substs, TraitRef, Ty, TyDefId, TypeCtor, ValueTyDefId, |
18 | }; | 18 | }; |
19 | use hir_expand::name::Name; | 19 | use hir_expand::name::Name; |
20 | 20 | ||
@@ -48,6 +48,12 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { | |||
48 | #[salsa::invoke(crate::callable_item_sig)] | 48 | #[salsa::invoke(crate::callable_item_sig)] |
49 | fn callable_item_signature(&self, def: CallableDef) -> PolyFnSig; | 49 | fn callable_item_signature(&self, def: CallableDef) -> PolyFnSig; |
50 | 50 | ||
51 | #[salsa::invoke(crate::lower::return_type_impl_traits)] | ||
52 | fn return_type_impl_traits( | ||
53 | &self, | ||
54 | def: FunctionId, | ||
55 | ) -> Option<Arc<Binders<ReturnTypeImplTraits>>>; | ||
56 | |||
51 | #[salsa::invoke(crate::lower::generic_predicates_for_param_query)] | 57 | #[salsa::invoke(crate::lower::generic_predicates_for_param_query)] |
52 | #[salsa::cycle(crate::lower::generic_predicates_for_param_recover)] | 58 | #[salsa::cycle(crate::lower::generic_predicates_for_param_recover)] |
53 | fn generic_predicates_for_param( | 59 | fn generic_predicates_for_param( |
@@ -80,6 +86,8 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { | |||
80 | #[salsa::interned] | 86 | #[salsa::interned] |
81 | fn intern_type_param_id(&self, param_id: TypeParamId) -> GlobalTypeParamId; | 87 | fn intern_type_param_id(&self, param_id: TypeParamId) -> GlobalTypeParamId; |
82 | #[salsa::interned] | 88 | #[salsa::interned] |
89 | fn intern_impl_trait_id(&self, id: OpaqueTyId) -> InternedOpaqueTyId; | ||
90 | #[salsa::interned] | ||
83 | fn intern_chalk_impl(&self, impl_: Impl) -> crate::traits::GlobalImplId; | 91 | fn intern_chalk_impl(&self, impl_: Impl) -> crate::traits::GlobalImplId; |
84 | #[salsa::interned] | 92 | #[salsa::interned] |
85 | fn intern_assoc_ty_value(&self, assoc_ty_value: AssocTyValue) -> crate::traits::AssocTyValueId; | 93 | fn intern_assoc_ty_value(&self, assoc_ty_value: AssocTyValue) -> crate::traits::AssocTyValueId; |
@@ -142,3 +150,7 @@ fn hir_database_is_object_safe() { | |||
142 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 150 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
143 | pub struct GlobalTypeParamId(salsa::InternId); | 151 | pub struct GlobalTypeParamId(salsa::InternId); |
144 | impl_intern_key!(GlobalTypeParamId); | 152 | impl_intern_key!(GlobalTypeParamId); |
153 | |||
154 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
155 | pub struct InternedOpaqueTyId(salsa::InternId); | ||
156 | impl_intern_key!(InternedOpaqueTyId); | ||
diff --git a/crates/ra_hir_ty/src/display.rs b/crates/ra_hir_ty/src/display.rs index b9c4d2e89..3c97e1354 100644 --- a/crates/ra_hir_ty/src/display.rs +++ b/crates/ra_hir_ty/src/display.rs | |||
@@ -4,7 +4,7 @@ use std::fmt; | |||
4 | 4 | ||
5 | use crate::{ | 5 | use crate::{ |
6 | db::HirDatabase, utils::generics, ApplicationTy, CallableDef, FnSig, GenericPredicate, | 6 | db::HirDatabase, utils::generics, ApplicationTy, CallableDef, FnSig, GenericPredicate, |
7 | Obligation, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, | 7 | Obligation, OpaqueTyId, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, |
8 | }; | 8 | }; |
9 | use hir_def::{ | 9 | use hir_def::{ |
10 | find_path, generics::TypeParamProvenance, item_scope::ItemInNs, AdtId, AssocContainerId, | 10 | find_path, generics::TypeParamProvenance, item_scope::ItemInNs, AdtId, AssocContainerId, |
@@ -359,6 +359,21 @@ impl HirDisplay for ApplicationTy { | |||
359 | write!(f, ">")?; | 359 | write!(f, ">")?; |
360 | } | 360 | } |
361 | } | 361 | } |
362 | TypeCtor::OpaqueType(opaque_ty_id) => { | ||
363 | let bounds = match opaque_ty_id { | ||
364 | OpaqueTyId::ReturnTypeImplTrait(func, idx) => { | ||
365 | let datas = | ||
366 | f.db.return_type_impl_traits(func).expect("impl trait id without data"); | ||
367 | let data = (*datas) | ||
368 | .as_ref() | ||
369 | .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone()); | ||
370 | data.clone().subst(&self.parameters) | ||
371 | } | ||
372 | }; | ||
373 | write!(f, "impl ")?; | ||
374 | write_bounds_like_dyn_trait(&bounds.value, f)?; | ||
375 | // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution | ||
376 | } | ||
362 | TypeCtor::Closure { .. } => { | 377 | TypeCtor::Closure { .. } => { |
363 | let sig = self.parameters[0].callable_sig(f.db); | 378 | let sig = self.parameters[0].callable_sig(f.db); |
364 | if let Some(sig) = sig { | 379 | if let Some(sig) = sig { |
@@ -427,14 +442,24 @@ impl HirDisplay for Ty { | |||
427 | } | 442 | } |
428 | } | 443 | } |
429 | Ty::Bound(idx) => write!(f, "?{}.{}", idx.debruijn.depth(), idx.index)?, | 444 | Ty::Bound(idx) => write!(f, "?{}.{}", idx.debruijn.depth(), idx.index)?, |
430 | Ty::Dyn(predicates) | Ty::Opaque(predicates) => { | 445 | Ty::Dyn(predicates) => { |
431 | match self { | 446 | write!(f, "dyn ")?; |
432 | Ty::Dyn(_) => write!(f, "dyn ")?, | ||
433 | Ty::Opaque(_) => write!(f, "impl ")?, | ||
434 | _ => unreachable!(), | ||
435 | }; | ||
436 | write_bounds_like_dyn_trait(predicates, f)?; | 447 | write_bounds_like_dyn_trait(predicates, f)?; |
437 | } | 448 | } |
449 | Ty::Opaque(opaque_ty) => { | ||
450 | let bounds = match opaque_ty.opaque_ty_id { | ||
451 | OpaqueTyId::ReturnTypeImplTrait(func, idx) => { | ||
452 | let datas = | ||
453 | f.db.return_type_impl_traits(func).expect("impl trait id without data"); | ||
454 | let data = (*datas) | ||
455 | .as_ref() | ||
456 | .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone()); | ||
457 | data.clone().subst(&opaque_ty.parameters) | ||
458 | } | ||
459 | }; | ||
460 | write!(f, "impl ")?; | ||
461 | write_bounds_like_dyn_trait(&bounds.value, f)?; | ||
462 | } | ||
438 | Ty::Unknown => write!(f, "{{unknown}}")?, | 463 | Ty::Unknown => write!(f, "{{unknown}}")?, |
439 | Ty::Infer(..) => write!(f, "_")?, | 464 | Ty::Infer(..) => write!(f, "_")?, |
440 | } | 465 | } |
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index dc77e88e5..d9bf3c2f0 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs | |||
@@ -39,8 +39,8 @@ use ra_syntax::SmolStr; | |||
39 | use super::{ | 39 | use super::{ |
40 | primitive::{FloatTy, IntTy}, | 40 | primitive::{FloatTy, IntTy}, |
41 | traits::{Guidance, Obligation, ProjectionPredicate, Solution}, | 41 | traits::{Guidance, Obligation, ProjectionPredicate, Solution}, |
42 | ApplicationTy, GenericPredicate, InEnvironment, ProjectionTy, Substs, TraitEnvironment, | 42 | ApplicationTy, InEnvironment, ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, |
43 | TraitRef, Ty, TypeCtor, TypeWalk, Uncertain, | 43 | TypeWalk, Uncertain, |
44 | }; | 44 | }; |
45 | use crate::{ | 45 | use crate::{ |
46 | db::HirDatabase, infer::diagnostics::InferenceDiagnostic, lower::ImplTraitLoweringMode, | 46 | db::HirDatabase, infer::diagnostics::InferenceDiagnostic, lower::ImplTraitLoweringMode, |
@@ -383,25 +383,6 @@ impl<'a> InferenceContext<'a> { | |||
383 | ) -> Ty { | 383 | ) -> Ty { |
384 | match assoc_ty { | 384 | match assoc_ty { |
385 | Some(res_assoc_ty) => { | 385 | Some(res_assoc_ty) => { |
386 | // FIXME: | ||
387 | // Check if inner_ty is is `impl Trait` and contained input TypeAlias id | ||
388 | // this is a workaround while Chalk assoc type projection doesn't always work yet, | ||
389 | // but once that is fixed I don't think we should keep this | ||
390 | // (we'll probably change how associated types are resolved anyway) | ||
391 | if let Ty::Opaque(ref predicates) = inner_ty { | ||
392 | for p in predicates.iter() { | ||
393 | if let GenericPredicate::Projection(projection) = p { | ||
394 | if projection.projection_ty.associated_ty == res_assoc_ty { | ||
395 | if let ty_app!(_, params) = &projection.ty { | ||
396 | if params.len() == 0 { | ||
397 | return projection.ty.clone(); | ||
398 | } | ||
399 | } | ||
400 | } | ||
401 | } | ||
402 | } | ||
403 | } | ||
404 | |||
405 | let ty = self.table.new_type_var(); | 386 | let ty = self.table.new_type_var(); |
406 | let builder = Substs::build_for_def(self.db, res_assoc_ty) | 387 | let builder = Substs::build_for_def(self.db, res_assoc_ty) |
407 | .push(inner_ty) | 388 | .push(inner_ty) |
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index 9fa8d3bdc..135976fcd 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs | |||
@@ -147,6 +147,12 @@ pub enum TypeCtor { | |||
147 | /// an **application type** like `(Iterator::Item)<T>`. | 147 | /// an **application type** like `(Iterator::Item)<T>`. |
148 | AssociatedType(TypeAliasId), | 148 | AssociatedType(TypeAliasId), |
149 | 149 | ||
150 | /// This represents a placeholder for an opaque type in situations where we | ||
151 | /// don't know the hidden type (i.e. currently almost always). This is | ||
152 | /// analogous to the `AssociatedType` type constructor. As with that one, | ||
153 | /// these are only produced by Chalk. | ||
154 | OpaqueType(OpaqueTyId), | ||
155 | |||
150 | /// The type of a specific closure. | 156 | /// The type of a specific closure. |
151 | /// | 157 | /// |
152 | /// The closure signature is stored in a `FnPtr` type in the first type | 158 | /// The closure signature is stored in a `FnPtr` type in the first type |
@@ -194,6 +200,14 @@ impl TypeCtor { | |||
194 | let generic_params = generics(db.upcast(), type_alias.into()); | 200 | let generic_params = generics(db.upcast(), type_alias.into()); |
195 | generic_params.len() | 201 | generic_params.len() |
196 | } | 202 | } |
203 | TypeCtor::OpaqueType(opaque_ty_id) => { | ||
204 | match opaque_ty_id { | ||
205 | OpaqueTyId::ReturnTypeImplTrait(func, _) => { | ||
206 | let generic_params = generics(db.upcast(), func.into()); | ||
207 | generic_params.len() | ||
208 | } | ||
209 | } | ||
210 | } | ||
197 | TypeCtor::FnPtr { num_args } => num_args as usize + 1, | 211 | TypeCtor::FnPtr { num_args } => num_args as usize + 1, |
198 | TypeCtor::Tuple { cardinality } => cardinality as usize, | 212 | TypeCtor::Tuple { cardinality } => cardinality as usize, |
199 | } | 213 | } |
@@ -220,6 +234,11 @@ impl TypeCtor { | |||
220 | TypeCtor::AssociatedType(type_alias) => { | 234 | TypeCtor::AssociatedType(type_alias) => { |
221 | Some(type_alias.lookup(db.upcast()).module(db.upcast()).krate) | 235 | Some(type_alias.lookup(db.upcast()).module(db.upcast()).krate) |
222 | } | 236 | } |
237 | TypeCtor::OpaqueType(opaque_ty_id) => match opaque_ty_id { | ||
238 | OpaqueTyId::ReturnTypeImplTrait(func, _) => { | ||
239 | Some(func.lookup(db.upcast()).module(db.upcast()).krate) | ||
240 | } | ||
241 | }, | ||
223 | } | 242 | } |
224 | } | 243 | } |
225 | 244 | ||
@@ -241,6 +260,7 @@ impl TypeCtor { | |||
241 | TypeCtor::Adt(adt) => Some(adt.into()), | 260 | TypeCtor::Adt(adt) => Some(adt.into()), |
242 | TypeCtor::FnDef(callable) => Some(callable.into()), | 261 | TypeCtor::FnDef(callable) => Some(callable.into()), |
243 | TypeCtor::AssociatedType(type_alias) => Some(type_alias.into()), | 262 | TypeCtor::AssociatedType(type_alias) => Some(type_alias.into()), |
263 | TypeCtor::OpaqueType(_impl_trait_id) => None, | ||
244 | } | 264 | } |
245 | } | 265 | } |
246 | } | 266 | } |
@@ -254,6 +274,12 @@ pub struct ApplicationTy { | |||
254 | pub parameters: Substs, | 274 | pub parameters: Substs, |
255 | } | 275 | } |
256 | 276 | ||
277 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] | ||
278 | pub struct OpaqueTy { | ||
279 | pub opaque_ty_id: OpaqueTyId, | ||
280 | pub parameters: Substs, | ||
281 | } | ||
282 | |||
257 | /// A "projection" type corresponds to an (unnormalized) | 283 | /// A "projection" type corresponds to an (unnormalized) |
258 | /// projection like `<P0 as Trait<P1..Pn>>::Foo`. Note that the | 284 | /// projection like `<P0 as Trait<P1..Pn>>::Foo`. Note that the |
259 | /// trait and all its parameters are fully known. | 285 | /// trait and all its parameters are fully known. |
@@ -308,6 +334,12 @@ pub enum Ty { | |||
308 | /// trait and all its parameters are fully known. | 334 | /// trait and all its parameters are fully known. |
309 | Projection(ProjectionTy), | 335 | Projection(ProjectionTy), |
310 | 336 | ||
337 | /// An opaque type (`impl Trait`). | ||
338 | /// | ||
339 | /// This is currently only used for return type impl trait; each instance of | ||
340 | /// `impl Trait` in a return type gets its own ID. | ||
341 | Opaque(OpaqueTy), | ||
342 | |||
311 | /// A placeholder for a type parameter; for example, `T` in `fn f<T>(x: T) | 343 | /// A placeholder for a type parameter; for example, `T` in `fn f<T>(x: T) |
312 | /// {}` when we're type-checking the body of that function. In this | 344 | /// {}` when we're type-checking the body of that function. In this |
313 | /// situation, we know this stands for *some* type, but don't know the exact | 345 | /// situation, we know this stands for *some* type, but don't know the exact |
@@ -332,12 +364,6 @@ pub enum Ty { | |||
332 | /// didn't seem worth the overhead yet. | 364 | /// didn't seem worth the overhead yet. |
333 | Dyn(Arc<[GenericPredicate]>), | 365 | Dyn(Arc<[GenericPredicate]>), |
334 | 366 | ||
335 | /// An opaque type (`impl Trait`). | ||
336 | /// | ||
337 | /// The predicates are quantified over the `Self` type; see `Ty::Dyn` for | ||
338 | /// more. | ||
339 | Opaque(Arc<[GenericPredicate]>), | ||
340 | |||
341 | /// A placeholder for a type which could not be computed; this is propagated | 367 | /// A placeholder for a type which could not be computed; this is propagated |
342 | /// to avoid useless error messages. Doubles as a placeholder where type | 368 | /// to avoid useless error messages. Doubles as a placeholder where type |
343 | /// variables are inserted before type checking, since we want to try to | 369 | /// variables are inserted before type checking, since we want to try to |
@@ -490,7 +516,7 @@ impl Deref for Substs { | |||
490 | } | 516 | } |
491 | } | 517 | } |
492 | 518 | ||
493 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | 519 | #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] |
494 | pub struct Binders<T> { | 520 | pub struct Binders<T> { |
495 | pub num_binders: usize, | 521 | pub num_binders: usize, |
496 | pub value: T, | 522 | pub value: T, |
@@ -534,6 +560,20 @@ impl<T: TypeWalk> Binders<T> { | |||
534 | } | 560 | } |
535 | } | 561 | } |
536 | 562 | ||
563 | impl<T: TypeWalk> TypeWalk for Binders<T> { | ||
564 | fn walk(&self, f: &mut impl FnMut(&Ty)) { | ||
565 | self.value.walk(f); | ||
566 | } | ||
567 | |||
568 | fn walk_mut_binders( | ||
569 | &mut self, | ||
570 | f: &mut impl FnMut(&mut Ty, DebruijnIndex), | ||
571 | binders: DebruijnIndex, | ||
572 | ) { | ||
573 | self.value.walk_mut_binders(f, binders.shifted_in()) | ||
574 | } | ||
575 | } | ||
576 | |||
537 | /// A trait with type parameters. This includes the `Self`, so this represents a concrete type implementing the trait. | 577 | /// A trait with type parameters. This includes the `Self`, so this represents a concrete type implementing the trait. |
538 | /// Name to be bikeshedded: TraitBound? TraitImplements? | 578 | /// Name to be bikeshedded: TraitBound? TraitImplements? |
539 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] | 579 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] |
@@ -947,11 +987,16 @@ impl TypeWalk for Ty { | |||
947 | t.walk(f); | 987 | t.walk(f); |
948 | } | 988 | } |
949 | } | 989 | } |
950 | Ty::Dyn(predicates) | Ty::Opaque(predicates) => { | 990 | Ty::Dyn(predicates) => { |
951 | for p in predicates.iter() { | 991 | for p in predicates.iter() { |
952 | p.walk(f); | 992 | p.walk(f); |
953 | } | 993 | } |
954 | } | 994 | } |
995 | Ty::Opaque(o_ty) => { | ||
996 | for t in o_ty.parameters.iter() { | ||
997 | t.walk(f); | ||
998 | } | ||
999 | } | ||
955 | Ty::Placeholder { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} | 1000 | Ty::Placeholder { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} |
956 | } | 1001 | } |
957 | f(self); | 1002 | f(self); |
@@ -969,13 +1014,48 @@ impl TypeWalk for Ty { | |||
969 | Ty::Projection(p_ty) => { | 1014 | Ty::Projection(p_ty) => { |
970 | p_ty.parameters.walk_mut_binders(f, binders); | 1015 | p_ty.parameters.walk_mut_binders(f, binders); |
971 | } | 1016 | } |
972 | Ty::Dyn(predicates) | Ty::Opaque(predicates) => { | 1017 | Ty::Dyn(predicates) => { |
973 | for p in make_mut_slice(predicates) { | 1018 | for p in make_mut_slice(predicates) { |
974 | p.walk_mut_binders(f, binders.shifted_in()); | 1019 | p.walk_mut_binders(f, binders.shifted_in()); |
975 | } | 1020 | } |
976 | } | 1021 | } |
1022 | Ty::Opaque(o_ty) => { | ||
1023 | o_ty.parameters.walk_mut_binders(f, binders); | ||
1024 | } | ||
977 | Ty::Placeholder { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} | 1025 | Ty::Placeholder { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} |
978 | } | 1026 | } |
979 | f(self, binders); | 1027 | f(self, binders); |
980 | } | 1028 | } |
981 | } | 1029 | } |
1030 | |||
1031 | impl<T: TypeWalk> TypeWalk for Vec<T> { | ||
1032 | fn walk(&self, f: &mut impl FnMut(&Ty)) { | ||
1033 | for t in self { | ||
1034 | t.walk(f); | ||
1035 | } | ||
1036 | } | ||
1037 | fn walk_mut_binders( | ||
1038 | &mut self, | ||
1039 | f: &mut impl FnMut(&mut Ty, DebruijnIndex), | ||
1040 | binders: DebruijnIndex, | ||
1041 | ) { | ||
1042 | for t in self { | ||
1043 | t.walk_mut_binders(f, binders); | ||
1044 | } | ||
1045 | } | ||
1046 | } | ||
1047 | |||
1048 | #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] | ||
1049 | pub enum OpaqueTyId { | ||
1050 | ReturnTypeImplTrait(hir_def::FunctionId, u16), | ||
1051 | } | ||
1052 | |||
1053 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] | ||
1054 | pub struct ReturnTypeImplTraits { | ||
1055 | pub(crate) impl_traits: Vec<ReturnTypeImplTrait>, | ||
1056 | } | ||
1057 | |||
1058 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] | ||
1059 | pub(crate) struct ReturnTypeImplTrait { | ||
1060 | pub(crate) bounds: Binders<Vec<GenericPredicate>>, | ||
1061 | } | ||
diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs index 35ac86a46..a05cbd7fc 100644 --- a/crates/ra_hir_ty/src/lower.rs +++ b/crates/ra_hir_ty/src/lower.rs | |||
@@ -21,8 +21,10 @@ use hir_def::{ | |||
21 | HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, | 21 | HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, |
22 | UnionId, VariantId, | 22 | UnionId, VariantId, |
23 | }; | 23 | }; |
24 | use hir_expand::name::Name; | ||
24 | use ra_arena::map::ArenaMap; | 25 | use ra_arena::map::ArenaMap; |
25 | use ra_db::CrateId; | 26 | use ra_db::CrateId; |
27 | use test_utils::mark; | ||
26 | 28 | ||
27 | use crate::{ | 29 | use crate::{ |
28 | db::HirDatabase, | 30 | db::HirDatabase, |
@@ -31,10 +33,10 @@ use crate::{ | |||
31 | all_super_trait_refs, associated_type_by_name_including_super_traits, generics, | 33 | all_super_trait_refs, associated_type_by_name_including_super_traits, generics, |
32 | make_mut_slice, variant_data, | 34 | make_mut_slice, variant_data, |
33 | }, | 35 | }, |
34 | Binders, BoundVar, DebruijnIndex, FnSig, GenericPredicate, PolyFnSig, ProjectionPredicate, | 36 | Binders, BoundVar, DebruijnIndex, FnSig, GenericPredicate, OpaqueTy, OpaqueTyId, PolyFnSig, |
35 | ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk, | 37 | ProjectionPredicate, ProjectionTy, ReturnTypeImplTrait, ReturnTypeImplTraits, Substs, |
38 | TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk, | ||
36 | }; | 39 | }; |
37 | use hir_expand::name::Name; | ||
38 | 40 | ||
39 | #[derive(Debug)] | 41 | #[derive(Debug)] |
40 | pub struct TyLoweringContext<'a> { | 42 | pub struct TyLoweringContext<'a> { |
@@ -47,7 +49,16 @@ pub struct TyLoweringContext<'a> { | |||
47 | /// possible currently, so this should be fine for now. | 49 | /// possible currently, so this should be fine for now. |
48 | pub type_param_mode: TypeParamLoweringMode, | 50 | pub type_param_mode: TypeParamLoweringMode, |
49 | pub impl_trait_mode: ImplTraitLoweringMode, | 51 | pub impl_trait_mode: ImplTraitLoweringMode, |
50 | pub impl_trait_counter: std::cell::Cell<u16>, | 52 | impl_trait_counter: std::cell::Cell<u16>, |
53 | /// When turning `impl Trait` into opaque types, we have to collect the | ||
54 | /// bounds at the same time to get the IDs correct (without becoming too | ||
55 | /// complicated). I don't like using interior mutability (as for the | ||
56 | /// counter), but I've tried and failed to make the lifetimes work for | ||
57 | /// passing around a `&mut TyLoweringContext`. The core problem is that | ||
58 | /// we're grouping the mutable data (the counter and this field) together | ||
59 | /// with the immutable context (the references to the DB and resolver). | ||
60 | /// Splitting this up would be a possible fix. | ||
61 | opaque_type_data: std::cell::RefCell<Vec<ReturnTypeImplTrait>>, | ||
51 | } | 62 | } |
52 | 63 | ||
53 | impl<'a> TyLoweringContext<'a> { | 64 | impl<'a> TyLoweringContext<'a> { |
@@ -56,26 +67,42 @@ impl<'a> TyLoweringContext<'a> { | |||
56 | let impl_trait_mode = ImplTraitLoweringMode::Disallowed; | 67 | let impl_trait_mode = ImplTraitLoweringMode::Disallowed; |
57 | let type_param_mode = TypeParamLoweringMode::Placeholder; | 68 | let type_param_mode = TypeParamLoweringMode::Placeholder; |
58 | let in_binders = DebruijnIndex::INNERMOST; | 69 | let in_binders = DebruijnIndex::INNERMOST; |
59 | Self { db, resolver, in_binders, impl_trait_mode, impl_trait_counter, type_param_mode } | 70 | let opaque_type_data = std::cell::RefCell::new(Vec::new()); |
71 | Self { | ||
72 | db, | ||
73 | resolver, | ||
74 | in_binders, | ||
75 | impl_trait_mode, | ||
76 | impl_trait_counter, | ||
77 | type_param_mode, | ||
78 | opaque_type_data, | ||
79 | } | ||
60 | } | 80 | } |
61 | 81 | ||
62 | pub fn with_shifted_in<T>( | 82 | pub fn with_debruijn<T>( |
63 | &self, | 83 | &self, |
64 | debruijn: DebruijnIndex, | 84 | debruijn: DebruijnIndex, |
65 | f: impl FnOnce(&TyLoweringContext) -> T, | 85 | f: impl FnOnce(&TyLoweringContext) -> T, |
66 | ) -> T { | 86 | ) -> T { |
87 | let opaque_ty_data_vec = self.opaque_type_data.replace(Vec::new()); | ||
67 | let new_ctx = Self { | 88 | let new_ctx = Self { |
68 | in_binders: self.in_binders.shifted_in_from(debruijn), | 89 | in_binders: debruijn, |
69 | impl_trait_counter: std::cell::Cell::new(self.impl_trait_counter.get()), | 90 | impl_trait_counter: std::cell::Cell::new(self.impl_trait_counter.get()), |
91 | opaque_type_data: std::cell::RefCell::new(opaque_ty_data_vec), | ||
70 | ..*self | 92 | ..*self |
71 | }; | 93 | }; |
72 | let result = f(&new_ctx); | 94 | let result = f(&new_ctx); |
73 | self.impl_trait_counter.set(new_ctx.impl_trait_counter.get()); | 95 | self.impl_trait_counter.set(new_ctx.impl_trait_counter.get()); |
96 | self.opaque_type_data.replace(new_ctx.opaque_type_data.into_inner()); | ||
74 | result | 97 | result |
75 | } | 98 | } |
76 | 99 | ||
77 | pub fn shifted_in(self, debruijn: DebruijnIndex) -> Self { | 100 | pub fn with_shifted_in<T>( |
78 | Self { in_binders: self.in_binders.shifted_in_from(debruijn), ..self } | 101 | &self, |
102 | debruijn: DebruijnIndex, | ||
103 | f: impl FnOnce(&TyLoweringContext) -> T, | ||
104 | ) -> T { | ||
105 | self.with_debruijn(self.in_binders.shifted_in_from(debruijn), f) | ||
79 | } | 106 | } |
80 | 107 | ||
81 | pub fn with_impl_trait_mode(self, impl_trait_mode: ImplTraitLoweringMode) -> Self { | 108 | pub fn with_impl_trait_mode(self, impl_trait_mode: ImplTraitLoweringMode) -> Self { |
@@ -167,20 +194,44 @@ impl Ty { | |||
167 | TypeRef::ImplTrait(bounds) => { | 194 | TypeRef::ImplTrait(bounds) => { |
168 | match ctx.impl_trait_mode { | 195 | match ctx.impl_trait_mode { |
169 | ImplTraitLoweringMode::Opaque => { | 196 | ImplTraitLoweringMode::Opaque => { |
170 | let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0)); | 197 | let idx = ctx.impl_trait_counter.get(); |
171 | let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| { | 198 | ctx.impl_trait_counter.set(idx + 1); |
172 | bounds | 199 | |
173 | .iter() | 200 | assert!(idx as usize == ctx.opaque_type_data.borrow().len()); |
174 | .flat_map(|b| { | 201 | // this dance is to make sure the data is in the right |
175 | GenericPredicate::from_type_bound(ctx, b, self_ty.clone()) | 202 | // place even if we encounter more opaque types while |
176 | }) | 203 | // lowering the bounds |
177 | .collect() | 204 | ctx.opaque_type_data |
178 | }); | 205 | .borrow_mut() |
179 | Ty::Opaque(predicates) | 206 | .push(ReturnTypeImplTrait { bounds: Binders::new(1, Vec::new()) }); |
207 | // We don't want to lower the bounds inside the binders | ||
208 | // we're currently in, because they don't end up inside | ||
209 | // those binders. E.g. when we have `impl Trait<impl | ||
210 | // OtherTrait<T>>`, the `impl OtherTrait<T>` can't refer | ||
211 | // to the self parameter from `impl Trait`, and the | ||
212 | // bounds aren't actually stored nested within each | ||
213 | // other, but separately. So if the `T` refers to a type | ||
214 | // parameter of the outer function, it's just one binder | ||
215 | // away instead of two. | ||
216 | let actual_opaque_type_data = ctx | ||
217 | .with_debruijn(DebruijnIndex::INNERMOST, |ctx| { | ||
218 | ReturnTypeImplTrait::from_hir(ctx, &bounds) | ||
219 | }); | ||
220 | ctx.opaque_type_data.borrow_mut()[idx as usize] = actual_opaque_type_data; | ||
221 | |||
222 | let func = match ctx.resolver.generic_def() { | ||
223 | Some(GenericDefId::FunctionId(f)) => f, | ||
224 | _ => panic!("opaque impl trait lowering in non-function"), | ||
225 | }; | ||
226 | let impl_trait_id = OpaqueTyId::ReturnTypeImplTrait(func, idx); | ||
227 | let generics = generics(ctx.db.upcast(), func.into()); | ||
228 | let parameters = Substs::bound_vars(&generics, ctx.in_binders); | ||
229 | Ty::Opaque(OpaqueTy { opaque_ty_id: impl_trait_id, parameters }) | ||
180 | } | 230 | } |
181 | ImplTraitLoweringMode::Param => { | 231 | ImplTraitLoweringMode::Param => { |
182 | let idx = ctx.impl_trait_counter.get(); | 232 | let idx = ctx.impl_trait_counter.get(); |
183 | ctx.impl_trait_counter.set(idx + 1); | 233 | // FIXME we're probably doing something wrong here |
234 | ctx.impl_trait_counter.set(idx + count_impl_traits(type_ref) as u16); | ||
184 | if let Some(def) = ctx.resolver.generic_def() { | 235 | if let Some(def) = ctx.resolver.generic_def() { |
185 | let generics = generics(ctx.db.upcast(), def); | 236 | let generics = generics(ctx.db.upcast(), def); |
186 | let param = generics | 237 | let param = generics |
@@ -197,7 +248,8 @@ impl Ty { | |||
197 | } | 248 | } |
198 | ImplTraitLoweringMode::Variable => { | 249 | ImplTraitLoweringMode::Variable => { |
199 | let idx = ctx.impl_trait_counter.get(); | 250 | let idx = ctx.impl_trait_counter.get(); |
200 | ctx.impl_trait_counter.set(idx + 1); | 251 | // FIXME we're probably doing something wrong here |
252 | ctx.impl_trait_counter.set(idx + count_impl_traits(type_ref) as u16); | ||
201 | let (parent_params, self_params, list_params, _impl_trait_params) = | 253 | let (parent_params, self_params, list_params, _impl_trait_params) = |
202 | if let Some(def) = ctx.resolver.generic_def() { | 254 | if let Some(def) = ctx.resolver.generic_def() { |
203 | let generics = generics(ctx.db.upcast(), def); | 255 | let generics = generics(ctx.db.upcast(), def); |
@@ -663,6 +715,30 @@ fn assoc_type_bindings_from_type_bound<'a>( | |||
663 | }) | 715 | }) |
664 | } | 716 | } |
665 | 717 | ||
718 | impl ReturnTypeImplTrait { | ||
719 | fn from_hir(ctx: &TyLoweringContext, bounds: &[TypeBound]) -> Self { | ||
720 | mark::hit!(lower_rpit); | ||
721 | let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0)); | ||
722 | let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| { | ||
723 | bounds | ||
724 | .iter() | ||
725 | .flat_map(|b| GenericPredicate::from_type_bound(ctx, b, self_ty.clone())) | ||
726 | .collect() | ||
727 | }); | ||
728 | ReturnTypeImplTrait { bounds: Binders::new(1, predicates) } | ||
729 | } | ||
730 | } | ||
731 | |||
732 | fn count_impl_traits(type_ref: &TypeRef) -> usize { | ||
733 | let mut count = 0; | ||
734 | type_ref.walk(&mut |type_ref| { | ||
735 | if matches!(type_ref, TypeRef::ImplTrait(_)) { | ||
736 | count += 1; | ||
737 | } | ||
738 | }); | ||
739 | count | ||
740 | } | ||
741 | |||
666 | /// Build the signature of a callable item (function, struct or enum variant). | 742 | /// Build the signature of a callable item (function, struct or enum variant). |
667 | pub fn callable_item_sig(db: &dyn HirDatabase, def: CallableDef) -> PolyFnSig { | 743 | pub fn callable_item_sig(db: &dyn HirDatabase, def: CallableDef) -> PolyFnSig { |
668 | match def { | 744 | match def { |
@@ -864,7 +940,9 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig { | |||
864 | .with_impl_trait_mode(ImplTraitLoweringMode::Variable) | 940 | .with_impl_trait_mode(ImplTraitLoweringMode::Variable) |
865 | .with_type_param_mode(TypeParamLoweringMode::Variable); | 941 | .with_type_param_mode(TypeParamLoweringMode::Variable); |
866 | let params = data.params.iter().map(|tr| Ty::from_hir(&ctx_params, tr)).collect::<Vec<_>>(); | 942 | let params = data.params.iter().map(|tr| Ty::from_hir(&ctx_params, tr)).collect::<Vec<_>>(); |
867 | let ctx_ret = ctx_params.with_impl_trait_mode(ImplTraitLoweringMode::Opaque); | 943 | let ctx_ret = TyLoweringContext::new(db, &resolver) |
944 | .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) | ||
945 | .with_type_param_mode(TypeParamLoweringMode::Variable); | ||
868 | let ret = Ty::from_hir(&ctx_ret, &data.ret_type); | 946 | let ret = Ty::from_hir(&ctx_ret, &data.ret_type); |
869 | let generics = generics(db.upcast(), def.into()); | 947 | let generics = generics(db.upcast(), def.into()); |
870 | let num_binders = generics.len(); | 948 | let num_binders = generics.len(); |
@@ -1084,3 +1162,25 @@ pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option< | |||
1084 | TraitRef::from_hir(&ctx, target_trait, Some(self_ty.value))?, | 1162 | TraitRef::from_hir(&ctx, target_trait, Some(self_ty.value))?, |
1085 | )) | 1163 | )) |
1086 | } | 1164 | } |
1165 | |||
1166 | pub(crate) fn return_type_impl_traits( | ||
1167 | db: &impl HirDatabase, | ||
1168 | def: hir_def::FunctionId, | ||
1169 | ) -> Option<Arc<Binders<ReturnTypeImplTraits>>> { | ||
1170 | // FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe | ||
1171 | let data = db.function_data(def); | ||
1172 | let resolver = def.resolver(db.upcast()); | ||
1173 | let ctx_ret = TyLoweringContext::new(db, &resolver) | ||
1174 | .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) | ||
1175 | .with_type_param_mode(TypeParamLoweringMode::Variable); | ||
1176 | let _ret = Ty::from_hir(&ctx_ret, &data.ret_type); | ||
1177 | let generics = generics(db.upcast(), def.into()); | ||
1178 | let num_binders = generics.len(); | ||
1179 | let return_type_impl_traits = | ||
1180 | ReturnTypeImplTraits { impl_traits: ctx_ret.opaque_type_data.into_inner() }; | ||
1181 | if return_type_impl_traits.impl_traits.is_empty() { | ||
1182 | None | ||
1183 | } else { | ||
1184 | Some(Arc::new(Binders::new(num_binders, return_type_impl_traits))) | ||
1185 | } | ||
1186 | } | ||
diff --git a/crates/ra_hir_ty/src/test_db.rs b/crates/ra_hir_ty/src/test_db.rs index 21a3bdfd1..e484968a0 100644 --- a/crates/ra_hir_ty/src/test_db.rs +++ b/crates/ra_hir_ty/src/test_db.rs | |||
@@ -7,9 +7,7 @@ use std::{ | |||
7 | 7 | ||
8 | use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId, ModuleId}; | 8 | use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId, ModuleId}; |
9 | use hir_expand::{db::AstDatabase, diagnostics::DiagnosticSink}; | 9 | use hir_expand::{db::AstDatabase, diagnostics::DiagnosticSink}; |
10 | use ra_db::{ | 10 | use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast}; |
11 | salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath, SourceDatabase, Upcast, | ||
12 | }; | ||
13 | use stdx::format_to; | 11 | use stdx::format_to; |
14 | 12 | ||
15 | use crate::{db::HirDatabase, diagnostics::Diagnostic, expr::ExprValidator}; | 13 | use crate::{db::HirDatabase, diagnostics::Diagnostic, expr::ExprValidator}; |
@@ -78,13 +76,6 @@ impl FileLoader for TestDB { | |||
78 | fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { | 76 | fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { |
79 | FileLoaderDelegate(self).relevant_crates(file_id) | 77 | FileLoaderDelegate(self).relevant_crates(file_id) |
80 | } | 78 | } |
81 | fn resolve_extern_path( | ||
82 | &self, | ||
83 | extern_id: ra_db::ExternSourceId, | ||
84 | relative_path: &RelativePath, | ||
85 | ) -> Option<FileId> { | ||
86 | FileLoaderDelegate(self).resolve_extern_path(extern_id, relative_path) | ||
87 | } | ||
88 | } | 79 | } |
89 | 80 | ||
90 | impl TestDB { | 81 | impl TestDB { |
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index e8778d419..0c538a62d 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs | |||
@@ -1110,7 +1110,6 @@ fn test() { | |||
1110 | } | 1110 | } |
1111 | 1111 | ||
1112 | #[test] | 1112 | #[test] |
1113 | #[ignore] | ||
1114 | fn impl_trait() { | 1113 | fn impl_trait() { |
1115 | assert_snapshot!( | 1114 | assert_snapshot!( |
1116 | infer(r#" | 1115 | infer(r#" |
@@ -1161,6 +1160,95 @@ fn test(x: impl Trait<u64>, y: &impl Trait<u64>) { | |||
1161 | } | 1160 | } |
1162 | 1161 | ||
1163 | #[test] | 1162 | #[test] |
1163 | fn simple_return_pos_impl_trait() { | ||
1164 | mark::check!(lower_rpit); | ||
1165 | assert_snapshot!( | ||
1166 | infer(r#" | ||
1167 | trait Trait<T> { | ||
1168 | fn foo(&self) -> T; | ||
1169 | } | ||
1170 | fn bar() -> impl Trait<u64> { loop {} } | ||
1171 | |||
1172 | fn test() { | ||
1173 | let a = bar(); | ||
1174 | a.foo(); | ||
1175 | } | ||
1176 | "#), | ||
1177 | @r###" | ||
1178 | 30..34 'self': &Self | ||
1179 | 72..83 '{ loop {} }': ! | ||
1180 | 74..81 'loop {}': ! | ||
1181 | 79..81 '{}': () | ||
1182 | 95..130 '{ ...o(); }': () | ||
1183 | 105..106 'a': impl Trait<u64> | ||
1184 | 109..112 'bar': fn bar() -> impl Trait<u64> | ||
1185 | 109..114 'bar()': impl Trait<u64> | ||
1186 | 120..121 'a': impl Trait<u64> | ||
1187 | 120..127 'a.foo()': u64 | ||
1188 | "### | ||
1189 | ); | ||
1190 | } | ||
1191 | |||
1192 | #[test] | ||
1193 | fn more_return_pos_impl_trait() { | ||
1194 | assert_snapshot!( | ||
1195 | infer(r#" | ||
1196 | trait Iterator { | ||
1197 | type Item; | ||
1198 | fn next(&mut self) -> Self::Item; | ||
1199 | } | ||
1200 | trait Trait<T> { | ||
1201 | fn foo(&self) -> T; | ||
1202 | } | ||
1203 | fn bar() -> (impl Iterator<Item = impl Trait<u32>>, impl Trait<u64>) { loop {} } | ||
1204 | fn baz<T>(t: T) -> (impl Iterator<Item = impl Trait<T>>, impl Trait<T>) { loop {} } | ||
1205 | |||
1206 | fn test() { | ||
1207 | let (a, b) = bar(); | ||
1208 | a.next().foo(); | ||
1209 | b.foo(); | ||
1210 | let (c, d) = baz(1u128); | ||
1211 | c.next().foo(); | ||
1212 | d.foo(); | ||
1213 | } | ||
1214 | "#), | ||
1215 | @r###" | ||
1216 | 50..54 'self': &mut Self | ||
1217 | 102..106 'self': &Self | ||
1218 | 185..196 '{ loop {} }': ({unknown}, {unknown}) | ||
1219 | 187..194 'loop {}': ! | ||
1220 | 192..194 '{}': () | ||
1221 | 207..208 't': T | ||
1222 | 269..280 '{ loop {} }': ({unknown}, {unknown}) | ||
1223 | 271..278 'loop {}': ! | ||
1224 | 276..278 '{}': () | ||
1225 | 292..414 '{ ...o(); }': () | ||
1226 | 302..308 '(a, b)': (impl Iterator<Item = impl Trait<u32>>, impl Trait<u64>) | ||
1227 | 303..304 'a': impl Iterator<Item = impl Trait<u32>> | ||
1228 | 306..307 'b': impl Trait<u64> | ||
1229 | 311..314 'bar': fn bar() -> (impl Iterator<Item = impl Trait<u32>>, impl Trait<u64>) | ||
1230 | 311..316 'bar()': (impl Iterator<Item = impl Trait<u32>>, impl Trait<u64>) | ||
1231 | 322..323 'a': impl Iterator<Item = impl Trait<u32>> | ||
1232 | 322..330 'a.next()': impl Trait<u32> | ||
1233 | 322..336 'a.next().foo()': u32 | ||
1234 | 342..343 'b': impl Trait<u64> | ||
1235 | 342..349 'b.foo()': u64 | ||
1236 | 359..365 '(c, d)': (impl Iterator<Item = impl Trait<u128>>, impl Trait<u128>) | ||
1237 | 360..361 'c': impl Iterator<Item = impl Trait<u128>> | ||
1238 | 363..364 'd': impl Trait<u128> | ||
1239 | 368..371 'baz': fn baz<u128>(u128) -> (impl Iterator<Item = impl Trait<u128>>, impl Trait<u128>) | ||
1240 | 368..378 'baz(1u128)': (impl Iterator<Item = impl Trait<u128>>, impl Trait<u128>) | ||
1241 | 372..377 '1u128': u128 | ||
1242 | 384..385 'c': impl Iterator<Item = impl Trait<u128>> | ||
1243 | 384..392 'c.next()': impl Trait<u128> | ||
1244 | 384..398 'c.next().foo()': u128 | ||
1245 | 404..405 'd': impl Trait<u128> | ||
1246 | 404..411 'd.foo()': u128 | ||
1247 | "### | ||
1248 | ); | ||
1249 | } | ||
1250 | |||
1251 | #[test] | ||
1164 | fn dyn_trait() { | 1252 | fn dyn_trait() { |
1165 | assert_snapshot!( | 1253 | assert_snapshot!( |
1166 | infer(r#" | 1254 | infer(r#" |
diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs index 61de3cc30..a72a82f5a 100644 --- a/crates/ra_hir_ty/src/traits/chalk.rs +++ b/crates/ra_hir_ty/src/traits/chalk.rs | |||
@@ -4,7 +4,7 @@ use std::sync::Arc; | |||
4 | use log::debug; | 4 | use log::debug; |
5 | 5 | ||
6 | use chalk_ir::{fold::shift::Shift, GenericArg, TypeName}; | 6 | use chalk_ir::{fold::shift::Shift, GenericArg, TypeName}; |
7 | use chalk_solve::rust_ir::{self, WellKnownTrait}; | 7 | use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait}; |
8 | 8 | ||
9 | use hir_def::{ | 9 | use hir_def::{ |
10 | lang_item::{lang_attr, LangItemTarget}, | 10 | lang_item::{lang_attr, LangItemTarget}, |
@@ -100,6 +100,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
100 | fn associated_ty_value(&self, id: AssociatedTyValueId) -> Arc<AssociatedTyValue> { | 100 | fn associated_ty_value(&self, id: AssociatedTyValueId) -> Arc<AssociatedTyValue> { |
101 | self.db.associated_ty_value(self.krate, id) | 101 | self.db.associated_ty_value(self.krate, id) |
102 | } | 102 | } |
103 | |||
103 | fn custom_clauses(&self) -> Vec<chalk_ir::ProgramClause<Interner>> { | 104 | fn custom_clauses(&self) -> Vec<chalk_ir::ProgramClause<Interner>> { |
104 | vec![] | 105 | vec![] |
105 | } | 106 | } |
@@ -130,11 +131,34 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
130 | self.db.program_clauses_for_chalk_env(self.krate, environment.clone()) | 131 | self.db.program_clauses_for_chalk_env(self.krate, environment.clone()) |
131 | } | 132 | } |
132 | 133 | ||
133 | fn opaque_ty_data( | 134 | fn opaque_ty_data(&self, id: chalk_ir::OpaqueTyId<Interner>) -> Arc<OpaqueTyDatum> { |
134 | &self, | 135 | let interned_id = crate::db::InternedOpaqueTyId::from(id); |
135 | _id: chalk_ir::OpaqueTyId<Interner>, | 136 | let full_id = self.db.lookup_intern_impl_trait_id(interned_id); |
136 | ) -> Arc<rust_ir::OpaqueTyDatum<Interner>> { | 137 | let (func, idx) = match full_id { |
137 | unimplemented!() | 138 | crate::OpaqueTyId::ReturnTypeImplTrait(func, idx) => (func, idx), |
139 | }; | ||
140 | let datas = | ||
141 | self.db.return_type_impl_traits(func).expect("impl trait id without impl traits"); | ||
142 | let data = &datas.value.impl_traits[idx as usize]; | ||
143 | let bound = OpaqueTyDatumBound { | ||
144 | bounds: make_binders( | ||
145 | data.bounds | ||
146 | .value | ||
147 | .iter() | ||
148 | .cloned() | ||
149 | .filter(|b| !b.is_error()) | ||
150 | .map(|b| b.to_chalk(self.db)) | ||
151 | .collect(), | ||
152 | 1, | ||
153 | ), | ||
154 | }; | ||
155 | let num_vars = datas.num_binders; | ||
156 | Arc::new(OpaqueTyDatum { opaque_ty_id: id, bound: make_binders(bound, num_vars) }) | ||
157 | } | ||
158 | |||
159 | fn hidden_opaque_type(&self, _id: chalk_ir::OpaqueTyId<Interner>) -> chalk_ir::Ty<Interner> { | ||
160 | // FIXME: actually provide the hidden type; it is relevant for auto traits | ||
161 | Ty::Unknown.to_chalk(self.db) | ||
138 | } | 162 | } |
139 | 163 | ||
140 | fn force_impl_for( | 164 | fn force_impl_for( |
@@ -150,10 +174,6 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
150 | // FIXME: implement actual object safety | 174 | // FIXME: implement actual object safety |
151 | true | 175 | true |
152 | } | 176 | } |
153 | |||
154 | fn hidden_opaque_type(&self, _id: chalk_ir::OpaqueTyId<Interner>) -> chalk_ir::Ty<Interner> { | ||
155 | Ty::Unknown.to_chalk(self.db) | ||
156 | } | ||
157 | } | 177 | } |
158 | 178 | ||
159 | pub(crate) fn program_clauses_for_chalk_env_query( | 179 | pub(crate) fn program_clauses_for_chalk_env_query( |
@@ -460,6 +480,18 @@ impl From<crate::traits::GlobalImplId> for ImplId { | |||
460 | } | 480 | } |
461 | } | 481 | } |
462 | 482 | ||
483 | impl From<OpaqueTyId> for crate::db::InternedOpaqueTyId { | ||
484 | fn from(id: OpaqueTyId) -> Self { | ||
485 | InternKey::from_intern_id(id.0) | ||
486 | } | ||
487 | } | ||
488 | |||
489 | impl From<crate::db::InternedOpaqueTyId> for OpaqueTyId { | ||
490 | fn from(id: crate::db::InternedOpaqueTyId) -> Self { | ||
491 | chalk_ir::OpaqueTyId(id.as_intern_id()) | ||
492 | } | ||
493 | } | ||
494 | |||
463 | impl From<rust_ir::AssociatedTyValueId<Interner>> for crate::traits::AssocTyValueId { | 495 | impl From<rust_ir::AssociatedTyValueId<Interner>> for crate::traits::AssocTyValueId { |
464 | fn from(id: rust_ir::AssociatedTyValueId<Interner>) -> Self { | 496 | fn from(id: rust_ir::AssociatedTyValueId<Interner>) -> Self { |
465 | Self::from_intern_id(id.0) | 497 | Self::from_intern_id(id.0) |
diff --git a/crates/ra_hir_ty/src/traits/chalk/interner.rs b/crates/ra_hir_ty/src/traits/chalk/interner.rs index e27074ba6..56aab640c 100644 --- a/crates/ra_hir_ty/src/traits/chalk/interner.rs +++ b/crates/ra_hir_ty/src/traits/chalk/interner.rs | |||
@@ -22,6 +22,8 @@ pub type AssociatedTyValueId = chalk_solve::rust_ir::AssociatedTyValueId<Interne | |||
22 | pub type AssociatedTyValue = chalk_solve::rust_ir::AssociatedTyValue<Interner>; | 22 | pub type AssociatedTyValue = chalk_solve::rust_ir::AssociatedTyValue<Interner>; |
23 | pub type FnDefId = chalk_ir::FnDefId<Interner>; | 23 | pub type FnDefId = chalk_ir::FnDefId<Interner>; |
24 | pub type FnDefDatum = chalk_solve::rust_ir::FnDefDatum<Interner>; | 24 | pub type FnDefDatum = chalk_solve::rust_ir::FnDefDatum<Interner>; |
25 | pub type OpaqueTyId = chalk_ir::OpaqueTyId<Interner>; | ||
26 | pub type OpaqueTyDatum = chalk_solve::rust_ir::OpaqueTyDatum<Interner>; | ||
25 | 27 | ||
26 | impl chalk_ir::interner::Interner for Interner { | 28 | impl chalk_ir::interner::Interner for Interner { |
27 | type InternedType = Box<chalk_ir::TyData<Self>>; // FIXME use Arc? | 29 | type InternedType = Box<chalk_ir::TyData<Self>>; // FIXME use Arc? |
diff --git a/crates/ra_hir_ty/src/traits/chalk/mapping.rs b/crates/ra_hir_ty/src/traits/chalk/mapping.rs index 9150f65d4..28a5fbe3e 100644 --- a/crates/ra_hir_ty/src/traits/chalk/mapping.rs +++ b/crates/ra_hir_ty/src/traits/chalk/mapping.rs | |||
@@ -16,8 +16,8 @@ use crate::{ | |||
16 | db::HirDatabase, | 16 | db::HirDatabase, |
17 | primitive::{FloatBitness, FloatTy, IntBitness, IntTy, Signedness, Uncertain}, | 17 | primitive::{FloatBitness, FloatTy, IntBitness, IntTy, Signedness, Uncertain}, |
18 | traits::{builtin, AssocTyValue, Canonical, Impl, Obligation}, | 18 | traits::{builtin, AssocTyValue, Canonical, Impl, Obligation}, |
19 | ApplicationTy, CallableDef, GenericPredicate, InEnvironment, ProjectionPredicate, ProjectionTy, | 19 | ApplicationTy, CallableDef, GenericPredicate, InEnvironment, OpaqueTy, OpaqueTyId, |
20 | Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, | 20 | ProjectionPredicate, ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, |
21 | }; | 21 | }; |
22 | 22 | ||
23 | use super::interner::*; | 23 | use super::interner::*; |
@@ -68,7 +68,16 @@ impl ToChalk for Ty { | |||
68 | let bounded_ty = chalk_ir::DynTy { bounds: make_binders(where_clauses, 1) }; | 68 | let bounded_ty = chalk_ir::DynTy { bounds: make_binders(where_clauses, 1) }; |
69 | chalk_ir::TyData::Dyn(bounded_ty).intern(&Interner) | 69 | chalk_ir::TyData::Dyn(bounded_ty).intern(&Interner) |
70 | } | 70 | } |
71 | Ty::Opaque(_) | Ty::Unknown => { | 71 | Ty::Opaque(opaque_ty) => { |
72 | let opaque_ty_id = opaque_ty.opaque_ty_id.to_chalk(db); | ||
73 | let substitution = opaque_ty.parameters.to_chalk(db); | ||
74 | chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { | ||
75 | opaque_ty_id, | ||
76 | substitution, | ||
77 | })) | ||
78 | .intern(&Interner) | ||
79 | } | ||
80 | Ty::Unknown => { | ||
72 | let substitution = chalk_ir::Substitution::empty(&Interner); | 81 | let substitution = chalk_ir::Substitution::empty(&Interner); |
73 | let name = TypeName::Error; | 82 | let name = TypeName::Error; |
74 | chalk_ir::ApplicationTy { name, substitution }.cast(&Interner).intern(&Interner) | 83 | chalk_ir::ApplicationTy { name, substitution }.cast(&Interner).intern(&Interner) |
@@ -98,7 +107,11 @@ impl ToChalk for Ty { | |||
98 | let parameters = from_chalk(db, proj.substitution); | 107 | let parameters = from_chalk(db, proj.substitution); |
99 | Ty::Projection(ProjectionTy { associated_ty, parameters }) | 108 | Ty::Projection(ProjectionTy { associated_ty, parameters }) |
100 | } | 109 | } |
101 | chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(_)) => unimplemented!(), | 110 | chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(opaque_ty)) => { |
111 | let impl_trait_id = from_chalk(db, opaque_ty.opaque_ty_id); | ||
112 | let parameters = from_chalk(db, opaque_ty.substitution); | ||
113 | Ty::Opaque(OpaqueTy { opaque_ty_id: impl_trait_id, parameters }) | ||
114 | } | ||
102 | chalk_ir::TyData::Function(chalk_ir::Fn { num_binders: _, substitution }) => { | 115 | chalk_ir::TyData::Function(chalk_ir::Fn { num_binders: _, substitution }) => { |
103 | let parameters: Substs = from_chalk(db, substitution); | 116 | let parameters: Substs = from_chalk(db, substitution); |
104 | Ty::Apply(ApplicationTy { | 117 | Ty::Apply(ApplicationTy { |
@@ -204,6 +217,21 @@ impl ToChalk for hir_def::TraitId { | |||
204 | } | 217 | } |
205 | } | 218 | } |
206 | 219 | ||
220 | impl ToChalk for OpaqueTyId { | ||
221 | type Chalk = chalk_ir::OpaqueTyId<Interner>; | ||
222 | |||
223 | fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::OpaqueTyId<Interner> { | ||
224 | db.intern_impl_trait_id(self).into() | ||
225 | } | ||
226 | |||
227 | fn from_chalk( | ||
228 | db: &dyn HirDatabase, | ||
229 | opaque_ty_id: chalk_ir::OpaqueTyId<Interner>, | ||
230 | ) -> OpaqueTyId { | ||
231 | db.lookup_intern_impl_trait_id(opaque_ty_id.into()) | ||
232 | } | ||
233 | } | ||
234 | |||
207 | impl ToChalk for TypeCtor { | 235 | impl ToChalk for TypeCtor { |
208 | type Chalk = TypeName<Interner>; | 236 | type Chalk = TypeName<Interner>; |
209 | 237 | ||
@@ -214,6 +242,11 @@ impl ToChalk for TypeCtor { | |||
214 | TypeName::AssociatedType(type_id) | 242 | TypeName::AssociatedType(type_id) |
215 | } | 243 | } |
216 | 244 | ||
245 | TypeCtor::OpaqueType(impl_trait_id) => { | ||
246 | let id = impl_trait_id.to_chalk(db); | ||
247 | TypeName::OpaqueType(id) | ||
248 | } | ||
249 | |||
217 | TypeCtor::Bool => TypeName::Scalar(Scalar::Bool), | 250 | TypeCtor::Bool => TypeName::Scalar(Scalar::Bool), |
218 | TypeCtor::Char => TypeName::Scalar(Scalar::Char), | 251 | TypeCtor::Char => TypeName::Scalar(Scalar::Char), |
219 | TypeCtor::Int(Uncertain::Known(int_ty)) => TypeName::Scalar(int_ty_to_chalk(int_ty)), | 252 | TypeCtor::Int(Uncertain::Known(int_ty)) => TypeName::Scalar(int_ty_to_chalk(int_ty)), |
@@ -252,7 +285,9 @@ impl ToChalk for TypeCtor { | |||
252 | match type_name { | 285 | match type_name { |
253 | TypeName::Adt(struct_id) => db.lookup_intern_type_ctor(struct_id.into()), | 286 | TypeName::Adt(struct_id) => db.lookup_intern_type_ctor(struct_id.into()), |
254 | TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)), | 287 | TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)), |
255 | TypeName::OpaqueType(_) => unreachable!(), | 288 | TypeName::OpaqueType(opaque_type_id) => { |
289 | TypeCtor::OpaqueType(from_chalk(db, opaque_type_id)) | ||
290 | } | ||
256 | 291 | ||
257 | TypeName::Scalar(Scalar::Bool) => TypeCtor::Bool, | 292 | TypeName::Scalar(Scalar::Bool) => TypeCtor::Bool, |
258 | TypeName::Scalar(Scalar::Char) => TypeCtor::Char, | 293 | TypeName::Scalar(Scalar::Char) => TypeCtor::Char, |
diff --git a/crates/ra_hir_ty/src/traits/chalk/tls.rs b/crates/ra_hir_ty/src/traits/chalk/tls.rs index d88828c7c..556af7098 100644 --- a/crates/ra_hir_ty/src/traits/chalk/tls.rs +++ b/crates/ra_hir_ty/src/traits/chalk/tls.rs | |||
@@ -69,6 +69,11 @@ impl DebugContext<'_> { | |||
69 | let name = self.0.type_alias_data(type_alias).name.clone(); | 69 | let name = self.0.type_alias_data(type_alias).name.clone(); |
70 | write!(f, "{}::{}", trait_name, name)?; | 70 | write!(f, "{}::{}", trait_name, name)?; |
71 | } | 71 | } |
72 | TypeCtor::OpaqueType(opaque_ty_id) => match opaque_ty_id { | ||
73 | crate::OpaqueTyId::ReturnTypeImplTrait(func, idx) => { | ||
74 | write!(f, "{{impl trait {} of {:?}}}", idx, func)?; | ||
75 | } | ||
76 | }, | ||
72 | TypeCtor::Closure { def, expr } => { | 77 | TypeCtor::Closure { def, expr } => { |
73 | write!(f, "{{closure {:?} in ", expr.into_raw())?; | 78 | write!(f, "{{closure {:?} in ", expr.into_raw())?; |
74 | match def { | 79 | match def { |
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 9636cd0d6..62df07459 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs | |||
@@ -13,14 +13,43 @@ use ra_ide_db::{ | |||
13 | use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; | 13 | use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; |
14 | 14 | ||
15 | use crate::{ | 15 | use crate::{ |
16 | display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, | 16 | display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel, ToNav}, |
17 | FilePosition, RangeInfo, | 17 | FilePosition, NavigationTarget, RangeInfo, |
18 | }; | 18 | }; |
19 | 19 | ||
20 | #[derive(Clone, Debug, PartialEq, Eq)] | ||
21 | pub struct HoverConfig { | ||
22 | pub implementations: bool, | ||
23 | } | ||
24 | |||
25 | impl Default for HoverConfig { | ||
26 | fn default() -> Self { | ||
27 | Self { implementations: true } | ||
28 | } | ||
29 | } | ||
30 | |||
31 | impl HoverConfig { | ||
32 | pub const NO_ACTIONS: Self = Self { implementations: false }; | ||
33 | |||
34 | pub fn any(&self) -> bool { | ||
35 | self.implementations | ||
36 | } | ||
37 | |||
38 | pub fn none(&self) -> bool { | ||
39 | !self.any() | ||
40 | } | ||
41 | } | ||
42 | |||
43 | #[derive(Debug, Clone)] | ||
44 | pub enum HoverAction { | ||
45 | Implementaion(FilePosition), | ||
46 | } | ||
47 | |||
20 | /// Contains the results when hovering over an item | 48 | /// Contains the results when hovering over an item |
21 | #[derive(Debug, Default)] | 49 | #[derive(Debug, Default)] |
22 | pub struct HoverResult { | 50 | pub struct HoverResult { |
23 | results: Vec<String>, | 51 | results: Vec<String>, |
52 | actions: Vec<HoverAction>, | ||
24 | } | 53 | } |
25 | 54 | ||
26 | impl HoverResult { | 55 | impl HoverResult { |
@@ -48,10 +77,20 @@ impl HoverResult { | |||
48 | &self.results | 77 | &self.results |
49 | } | 78 | } |
50 | 79 | ||
80 | pub fn actions(&self) -> &[HoverAction] { | ||
81 | &self.actions | ||
82 | } | ||
83 | |||
84 | pub fn push_action(&mut self, action: HoverAction) { | ||
85 | self.actions.push(action); | ||
86 | } | ||
87 | |||
51 | /// Returns the results converted into markup | 88 | /// Returns the results converted into markup |
52 | /// for displaying in a UI | 89 | /// for displaying in a UI |
90 | /// | ||
91 | /// Does not process actions! | ||
53 | pub fn to_markup(&self) -> String { | 92 | pub fn to_markup(&self) -> String { |
54 | self.results.join("\n\n---\n") | 93 | self.results.join("\n\n___\n") |
55 | } | 94 | } |
56 | } | 95 | } |
57 | 96 | ||
@@ -82,6 +121,10 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn | |||
82 | res.extend(hover_text_from_name_kind(db, name_kind)); | 121 | res.extend(hover_text_from_name_kind(db, name_kind)); |
83 | 122 | ||
84 | if !res.is_empty() { | 123 | if !res.is_empty() { |
124 | if let Some(action) = show_implementations_action(db, name_kind) { | ||
125 | res.push_action(action); | ||
126 | } | ||
127 | |||
85 | return Some(RangeInfo::new(range, res)); | 128 | return Some(RangeInfo::new(range, res)); |
86 | } | 129 | } |
87 | } | 130 | } |
@@ -112,6 +155,26 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn | |||
112 | Some(RangeInfo::new(range, res)) | 155 | Some(RangeInfo::new(range, res)) |
113 | } | 156 | } |
114 | 157 | ||
158 | fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { | ||
159 | fn to_action(nav_target: NavigationTarget) -> HoverAction { | ||
160 | HoverAction::Implementaion(FilePosition { | ||
161 | file_id: nav_target.file_id(), | ||
162 | offset: nav_target.range().start(), | ||
163 | }) | ||
164 | } | ||
165 | |||
166 | match def { | ||
167 | Definition::ModuleDef(it) => match it { | ||
168 | ModuleDef::Adt(Adt::Struct(it)) => Some(to_action(it.to_nav(db))), | ||
169 | ModuleDef::Adt(Adt::Union(it)) => Some(to_action(it.to_nav(db))), | ||
170 | ModuleDef::Adt(Adt::Enum(it)) => Some(to_action(it.to_nav(db))), | ||
171 | ModuleDef::Trait(it) => Some(to_action(it.to_nav(db))), | ||
172 | _ => None, | ||
173 | }, | ||
174 | _ => None, | ||
175 | } | ||
176 | } | ||
177 | |||
115 | fn hover_text( | 178 | fn hover_text( |
116 | docs: Option<String>, | 179 | docs: Option<String>, |
117 | desc: Option<String>, | 180 | desc: Option<String>, |
@@ -228,6 +291,8 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | |||
228 | 291 | ||
229 | #[cfg(test)] | 292 | #[cfg(test)] |
230 | mod tests { | 293 | mod tests { |
294 | use super::*; | ||
295 | |||
231 | use ra_db::FileLoader; | 296 | use ra_db::FileLoader; |
232 | use ra_syntax::TextRange; | 297 | use ra_syntax::TextRange; |
233 | 298 | ||
@@ -241,7 +306,14 @@ mod tests { | |||
241 | s.map(trim_markup) | 306 | s.map(trim_markup) |
242 | } | 307 | } |
243 | 308 | ||
244 | fn check_hover_result(fixture: &str, expected: &[&str]) -> String { | 309 | fn assert_impl_action(action: &HoverAction, position: u32) { |
310 | let offset = match action { | ||
311 | HoverAction::Implementaion(pos) => pos.offset, | ||
312 | }; | ||
313 | assert_eq!(offset, position.into()); | ||
314 | } | ||
315 | |||
316 | fn check_hover_result(fixture: &str, expected: &[&str]) -> (String, Vec<HoverAction>) { | ||
245 | let (analysis, position) = analysis_and_position(fixture); | 317 | let (analysis, position) = analysis_and_position(fixture); |
246 | let hover = analysis.hover(position).unwrap().unwrap(); | 318 | let hover = analysis.hover(position).unwrap().unwrap(); |
247 | let mut results = Vec::from(hover.info.results()); | 319 | let mut results = Vec::from(hover.info.results()); |
@@ -256,7 +328,7 @@ mod tests { | |||
256 | assert_eq!(hover.info.len(), expected.len()); | 328 | assert_eq!(hover.info.len(), expected.len()); |
257 | 329 | ||
258 | let content = analysis.db.file_text(position.file_id); | 330 | let content = analysis.db.file_text(position.file_id); |
259 | content[hover.range].to_string() | 331 | (content[hover.range].to_string(), hover.info.actions().to_vec()) |
260 | } | 332 | } |
261 | 333 | ||
262 | fn check_hover_no_result(fixture: &str) { | 334 | fn check_hover_no_result(fixture: &str) { |
@@ -746,7 +818,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
746 | 818 | ||
747 | #[test] | 819 | #[test] |
748 | fn test_hover_through_macro() { | 820 | fn test_hover_through_macro() { |
749 | let hover_on = check_hover_result( | 821 | let (hover_on, _) = check_hover_result( |
750 | " | 822 | " |
751 | //- /lib.rs | 823 | //- /lib.rs |
752 | macro_rules! id { | 824 | macro_rules! id { |
@@ -767,7 +839,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
767 | 839 | ||
768 | #[test] | 840 | #[test] |
769 | fn test_hover_through_expr_in_macro() { | 841 | fn test_hover_through_expr_in_macro() { |
770 | let hover_on = check_hover_result( | 842 | let (hover_on, _) = check_hover_result( |
771 | " | 843 | " |
772 | //- /lib.rs | 844 | //- /lib.rs |
773 | macro_rules! id { | 845 | macro_rules! id { |
@@ -785,7 +857,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
785 | 857 | ||
786 | #[test] | 858 | #[test] |
787 | fn test_hover_through_expr_in_macro_recursive() { | 859 | fn test_hover_through_expr_in_macro_recursive() { |
788 | let hover_on = check_hover_result( | 860 | let (hover_on, _) = check_hover_result( |
789 | " | 861 | " |
790 | //- /lib.rs | 862 | //- /lib.rs |
791 | macro_rules! id_deep { | 863 | macro_rules! id_deep { |
@@ -806,7 +878,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
806 | 878 | ||
807 | #[test] | 879 | #[test] |
808 | fn test_hover_through_func_in_macro_recursive() { | 880 | fn test_hover_through_func_in_macro_recursive() { |
809 | let hover_on = check_hover_result( | 881 | let (hover_on, _) = check_hover_result( |
810 | " | 882 | " |
811 | //- /lib.rs | 883 | //- /lib.rs |
812 | macro_rules! id_deep { | 884 | macro_rules! id_deep { |
@@ -830,7 +902,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
830 | 902 | ||
831 | #[test] | 903 | #[test] |
832 | fn test_hover_through_literal_string_in_macro() { | 904 | fn test_hover_through_literal_string_in_macro() { |
833 | let hover_on = check_hover_result( | 905 | let (hover_on, _) = check_hover_result( |
834 | r#" | 906 | r#" |
835 | //- /lib.rs | 907 | //- /lib.rs |
836 | macro_rules! arr { | 908 | macro_rules! arr { |
@@ -849,7 +921,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
849 | 921 | ||
850 | #[test] | 922 | #[test] |
851 | fn test_hover_through_assert_macro() { | 923 | fn test_hover_through_assert_macro() { |
852 | let hover_on = check_hover_result( | 924 | let (hover_on, _) = check_hover_result( |
853 | r#" | 925 | r#" |
854 | //- /lib.rs | 926 | //- /lib.rs |
855 | #[rustc_builtin_macro] | 927 | #[rustc_builtin_macro] |
@@ -925,13 +997,14 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
925 | 997 | ||
926 | #[test] | 998 | #[test] |
927 | fn test_hover_trait_show_qualifiers() { | 999 | fn test_hover_trait_show_qualifiers() { |
928 | check_hover_result( | 1000 | let (_, actions) = check_hover_result( |
929 | " | 1001 | " |
930 | //- /lib.rs | 1002 | //- /lib.rs |
931 | unsafe trait foo<|>() {} | 1003 | unsafe trait foo<|>() {} |
932 | ", | 1004 | ", |
933 | &["unsafe trait foo"], | 1005 | &["unsafe trait foo"], |
934 | ); | 1006 | ); |
1007 | assert_impl_action(&actions[0], 13); | ||
935 | } | 1008 | } |
936 | 1009 | ||
937 | #[test] | 1010 | #[test] |
@@ -1052,4 +1125,55 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
1052 | &["Bar\n```\n\n```rust\nfn foo(&self)\n```\n___\n\nDo the foo"], | 1125 | &["Bar\n```\n\n```rust\nfn foo(&self)\n```\n___\n\nDo the foo"], |
1053 | ); | 1126 | ); |
1054 | } | 1127 | } |
1128 | |||
1129 | #[test] | ||
1130 | fn test_hover_trait_has_impl_action() { | ||
1131 | let (_, actions) = check_hover_result( | ||
1132 | " | ||
1133 | //- /lib.rs | ||
1134 | trait foo<|>() {} | ||
1135 | ", | ||
1136 | &["trait foo"], | ||
1137 | ); | ||
1138 | assert_impl_action(&actions[0], 6); | ||
1139 | } | ||
1140 | |||
1141 | #[test] | ||
1142 | fn test_hover_struct_has_impl_action() { | ||
1143 | let (_, actions) = check_hover_result( | ||
1144 | " | ||
1145 | //- /lib.rs | ||
1146 | struct foo<|>() {} | ||
1147 | ", | ||
1148 | &["struct foo"], | ||
1149 | ); | ||
1150 | assert_impl_action(&actions[0], 7); | ||
1151 | } | ||
1152 | |||
1153 | #[test] | ||
1154 | fn test_hover_union_has_impl_action() { | ||
1155 | let (_, actions) = check_hover_result( | ||
1156 | " | ||
1157 | //- /lib.rs | ||
1158 | union foo<|>() {} | ||
1159 | ", | ||
1160 | &["union foo"], | ||
1161 | ); | ||
1162 | assert_impl_action(&actions[0], 6); | ||
1163 | } | ||
1164 | |||
1165 | #[test] | ||
1166 | fn test_hover_enum_has_impl_action() { | ||
1167 | let (_, actions) = check_hover_result( | ||
1168 | " | ||
1169 | //- /lib.rs | ||
1170 | enum foo<|>() { | ||
1171 | A, | ||
1172 | B | ||
1173 | } | ||
1174 | ", | ||
1175 | &["enum foo"], | ||
1176 | ); | ||
1177 | assert_impl_action(&actions[0], 5); | ||
1178 | } | ||
1055 | } | 1179 | } |
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 34c2d75fe..a56718d3f 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs | |||
@@ -66,7 +66,7 @@ pub use crate::{ | |||
66 | display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, | 66 | display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, |
67 | expand_macro::ExpandedMacro, | 67 | expand_macro::ExpandedMacro, |
68 | folding_ranges::{Fold, FoldKind}, | 68 | folding_ranges::{Fold, FoldKind}, |
69 | hover::HoverResult, | 69 | hover::{HoverAction, HoverConfig, HoverResult}, |
70 | inlay_hints::{InlayHint, InlayHintsConfig, InlayKind}, | 70 | inlay_hints::{InlayHint, InlayHintsConfig, InlayKind}, |
71 | references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult}, | 71 | references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult}, |
72 | runnables::{Runnable, RunnableKind, TestId}, | 72 | runnables::{Runnable, RunnableKind, TestId}, |
diff --git a/crates/ra_ide_db/src/change.rs b/crates/ra_ide_db/src/change.rs index 8446ef88e..5dbe1c1b7 100644 --- a/crates/ra_ide_db/src/change.rs +++ b/crates/ra_ide_db/src/change.rs | |||
@@ -334,6 +334,7 @@ impl RootDatabase { | |||
334 | hir::db::CrateLangItemsQuery | 334 | hir::db::CrateLangItemsQuery |
335 | hir::db::LangItemQuery | 335 | hir::db::LangItemQuery |
336 | hir::db::DocumentationQuery | 336 | hir::db::DocumentationQuery |
337 | hir::db::ImportMapQuery | ||
337 | 338 | ||
338 | // InternDatabase | 339 | // InternDatabase |
339 | hir::db::InternFunctionQuery | 340 | hir::db::InternFunctionQuery |
@@ -369,6 +370,7 @@ impl RootDatabase { | |||
369 | hir::db::ImplDatumQuery | 370 | hir::db::ImplDatumQuery |
370 | hir::db::AssociatedTyValueQuery | 371 | hir::db::AssociatedTyValueQuery |
371 | hir::db::TraitSolveQuery | 372 | hir::db::TraitSolveQuery |
373 | hir::db::ReturnTypeImplTraitsQuery | ||
372 | 374 | ||
373 | // SymbolsDatabase | 375 | // SymbolsDatabase |
374 | crate::symbol_index::FileSymbolsQuery | 376 | crate::symbol_index::FileSymbolsQuery |
diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs index 8b06cbfc5..1db60b87f 100644 --- a/crates/ra_ide_db/src/defs.rs +++ b/crates/ra_ide_db/src/defs.rs | |||
@@ -18,7 +18,7 @@ use ra_syntax::{ | |||
18 | use crate::RootDatabase; | 18 | use crate::RootDatabase; |
19 | 19 | ||
20 | // FIXME: a more precise name would probably be `Symbol`? | 20 | // FIXME: a more precise name would probably be `Symbol`? |
21 | #[derive(Debug, PartialEq, Eq)] | 21 | #[derive(Debug, PartialEq, Eq, Copy, Clone)] |
22 | pub enum Definition { | 22 | pub enum Definition { |
23 | Macro(MacroDef), | 23 | Macro(MacroDef), |
24 | Field(Field), | 24 | Field(Field), |
diff --git a/crates/ra_ide_db/src/lib.rs b/crates/ra_ide_db/src/lib.rs index 93d5891a0..727d743b5 100644 --- a/crates/ra_ide_db/src/lib.rs +++ b/crates/ra_ide_db/src/lib.rs | |||
@@ -16,8 +16,8 @@ use std::sync::Arc; | |||
16 | use hir::db::{AstDatabase, DefDatabase}; | 16 | use hir::db::{AstDatabase, DefDatabase}; |
17 | use ra_db::{ | 17 | use ra_db::{ |
18 | salsa::{self, Database, Durability}, | 18 | salsa::{self, Database, Durability}, |
19 | Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath, | 19 | Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, |
20 | SourceDatabase, SourceRootId, Upcast, | 20 | SourceRootId, Upcast, |
21 | }; | 21 | }; |
22 | use rustc_hash::FxHashMap; | 22 | use rustc_hash::FxHashMap; |
23 | 23 | ||
@@ -63,13 +63,6 @@ impl FileLoader for RootDatabase { | |||
63 | fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { | 63 | fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { |
64 | FileLoaderDelegate(self).relevant_crates(file_id) | 64 | FileLoaderDelegate(self).relevant_crates(file_id) |
65 | } | 65 | } |
66 | fn resolve_extern_path( | ||
67 | &self, | ||
68 | extern_id: ra_db::ExternSourceId, | ||
69 | relative_path: &RelativePath, | ||
70 | ) -> Option<FileId> { | ||
71 | FileLoaderDelegate(self).resolve_extern_path(extern_id, relative_path) | ||
72 | } | ||
73 | } | 66 | } |
74 | 67 | ||
75 | impl salsa::Database for RootDatabase { | 68 | impl salsa::Database for RootDatabase { |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 23168c3ae..8d6efdbe8 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -11,7 +11,7 @@ use std::{ffi::OsString, path::PathBuf}; | |||
11 | 11 | ||
12 | use lsp_types::ClientCapabilities; | 12 | use lsp_types::ClientCapabilities; |
13 | use ra_flycheck::FlycheckConfig; | 13 | use ra_flycheck::FlycheckConfig; |
14 | use ra_ide::{AssistConfig, CompletionConfig, InlayHintsConfig}; | 14 | use ra_ide::{AssistConfig, CompletionConfig, HoverConfig, InlayHintsConfig}; |
15 | use ra_project_model::{CargoConfig, JsonProject, ProjectManifest}; | 15 | use ra_project_model::{CargoConfig, JsonProject, ProjectManifest}; |
16 | use serde::Deserialize; | 16 | use serde::Deserialize; |
17 | 17 | ||
@@ -34,6 +34,7 @@ pub struct Config { | |||
34 | pub assist: AssistConfig, | 34 | pub assist: AssistConfig, |
35 | pub call_info_full: bool, | 35 | pub call_info_full: bool, |
36 | pub lens: LensConfig, | 36 | pub lens: LensConfig, |
37 | pub hover: HoverConfig, | ||
37 | 38 | ||
38 | pub with_sysroot: bool, | 39 | pub with_sysroot: bool, |
39 | pub linked_projects: Vec<LinkedProject>, | 40 | pub linked_projects: Vec<LinkedProject>, |
@@ -124,6 +125,7 @@ pub struct ClientCapsConfig { | |||
124 | pub work_done_progress: bool, | 125 | pub work_done_progress: bool, |
125 | pub code_action_group: bool, | 126 | pub code_action_group: bool, |
126 | pub resolve_code_action: bool, | 127 | pub resolve_code_action: bool, |
128 | pub hover_actions: bool, | ||
127 | } | 129 | } |
128 | 130 | ||
129 | impl Default for Config { | 131 | impl Default for Config { |
@@ -162,6 +164,7 @@ impl Default for Config { | |||
162 | assist: AssistConfig::default(), | 164 | assist: AssistConfig::default(), |
163 | call_info_full: true, | 165 | call_info_full: true, |
164 | lens: LensConfig::default(), | 166 | lens: LensConfig::default(), |
167 | hover: HoverConfig::default(), | ||
165 | linked_projects: Vec::new(), | 168 | linked_projects: Vec::new(), |
166 | } | 169 | } |
167 | } | 170 | } |
@@ -278,6 +281,14 @@ impl Config { | |||
278 | } | 281 | } |
279 | } | 282 | } |
280 | 283 | ||
284 | let mut use_hover_actions = false; | ||
285 | set(value, "/hoverActions/enable", &mut use_hover_actions); | ||
286 | if use_hover_actions { | ||
287 | set(value, "/hoverActions/implementations", &mut self.hover.implementations); | ||
288 | } else { | ||
289 | self.hover = HoverConfig::NO_ACTIONS; | ||
290 | } | ||
291 | |||
281 | log::info!("Config::update() = {:#?}", self); | 292 | log::info!("Config::update() = {:#?}", self); |
282 | 293 | ||
283 | fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> { | 294 | fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> { |
@@ -331,17 +342,15 @@ impl Config { | |||
331 | 342 | ||
332 | self.assist.allow_snippets(false); | 343 | self.assist.allow_snippets(false); |
333 | if let Some(experimental) = &caps.experimental { | 344 | if let Some(experimental) = &caps.experimental { |
334 | let snippet_text_edit = | 345 | let get_bool = |
335 | experimental.get("snippetTextEdit").and_then(|it| it.as_bool()) == Some(true); | 346 | |index: &str| experimental.get(index).and_then(|it| it.as_bool()) == Some(true); |
336 | self.assist.allow_snippets(snippet_text_edit); | ||
337 | 347 | ||
338 | let code_action_group = | 348 | let snippet_text_edit = get_bool("snippetTextEdit"); |
339 | experimental.get("codeActionGroup").and_then(|it| it.as_bool()) == Some(true); | 349 | self.assist.allow_snippets(snippet_text_edit); |
340 | self.client_caps.code_action_group = code_action_group; | ||
341 | 350 | ||
342 | let resolve_code_action = | 351 | self.client_caps.code_action_group = get_bool("codeActionGroup"); |
343 | experimental.get("resolveCodeAction").and_then(|it| it.as_bool()) == Some(true); | 352 | self.client_caps.resolve_code_action = get_bool("resolveCodeAction"); |
344 | self.client_caps.resolve_code_action = resolve_code_action; | 353 | self.client_caps.hover_actions = get_bool("hoverActions"); |
345 | } | 354 | } |
346 | } | 355 | } |
347 | } | 356 | } |
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 3b957534d..1371f6cb4 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs | |||
@@ -260,3 +260,35 @@ pub struct SnippetTextEdit { | |||
260 | #[serde(skip_serializing_if = "Option::is_none")] | 260 | #[serde(skip_serializing_if = "Option::is_none")] |
261 | pub insert_text_format: Option<lsp_types::InsertTextFormat>, | 261 | pub insert_text_format: Option<lsp_types::InsertTextFormat>, |
262 | } | 262 | } |
263 | |||
264 | pub enum HoverRequest {} | ||
265 | |||
266 | impl Request for HoverRequest { | ||
267 | type Params = lsp_types::HoverParams; | ||
268 | type Result = Option<Hover>; | ||
269 | const METHOD: &'static str = "textDocument/hover"; | ||
270 | } | ||
271 | |||
272 | #[derive(Debug, PartialEq, Clone, Deserialize, Serialize)] | ||
273 | pub struct Hover { | ||
274 | #[serde(flatten)] | ||
275 | pub hover: lsp_types::Hover, | ||
276 | #[serde(skip_serializing_if = "Vec::is_empty")] | ||
277 | pub actions: Vec<CommandLinkGroup>, | ||
278 | } | ||
279 | |||
280 | #[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)] | ||
281 | pub struct CommandLinkGroup { | ||
282 | #[serde(skip_serializing_if = "Option::is_none")] | ||
283 | pub title: Option<String>, | ||
284 | pub commands: Vec<CommandLink>, | ||
285 | } | ||
286 | |||
287 | // LSP v3.15 Command does not have a `tooltip` field, vscode supports one. | ||
288 | #[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)] | ||
289 | pub struct CommandLink { | ||
290 | #[serde(flatten)] | ||
291 | pub command: lsp_types::Command, | ||
292 | #[serde(skip_serializing_if = "Option::is_none")] | ||
293 | pub tooltip: Option<String>, | ||
294 | } | ||
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index e60337b8e..752dbf145 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -510,6 +510,7 @@ fn on_request( | |||
510 | .on::<lsp_ext::InlayHints>(handlers::handle_inlay_hints)? | 510 | .on::<lsp_ext::InlayHints>(handlers::handle_inlay_hints)? |
511 | .on::<lsp_ext::CodeActionRequest>(handlers::handle_code_action)? | 511 | .on::<lsp_ext::CodeActionRequest>(handlers::handle_code_action)? |
512 | .on::<lsp_ext::ResolveCodeActionRequest>(handlers::handle_resolve_code_action)? | 512 | .on::<lsp_ext::ResolveCodeActionRequest>(handlers::handle_resolve_code_action)? |
513 | .on::<lsp_ext::HoverRequest>(handlers::handle_hover)? | ||
513 | .on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting)? | 514 | .on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting)? |
514 | .on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)? | 515 | .on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)? |
515 | .on::<lsp_types::request::WorkspaceSymbol>(handlers::handle_workspace_symbol)? | 516 | .on::<lsp_types::request::WorkspaceSymbol>(handlers::handle_workspace_symbol)? |
@@ -521,7 +522,6 @@ fn on_request( | |||
521 | .on::<lsp_types::request::CodeLensResolve>(handlers::handle_code_lens_resolve)? | 522 | .on::<lsp_types::request::CodeLensResolve>(handlers::handle_code_lens_resolve)? |
522 | .on::<lsp_types::request::FoldingRangeRequest>(handlers::handle_folding_range)? | 523 | .on::<lsp_types::request::FoldingRangeRequest>(handlers::handle_folding_range)? |
523 | .on::<lsp_types::request::SignatureHelpRequest>(handlers::handle_signature_help)? | 524 | .on::<lsp_types::request::SignatureHelpRequest>(handlers::handle_signature_help)? |
524 | .on::<lsp_types::request::HoverRequest>(handlers::handle_hover)? | ||
525 | .on::<lsp_types::request::PrepareRenameRequest>(handlers::handle_prepare_rename)? | 525 | .on::<lsp_types::request::PrepareRenameRequest>(handlers::handle_prepare_rename)? |
526 | .on::<lsp_types::request::Rename>(handlers::handle_rename)? | 526 | .on::<lsp_types::request::Rename>(handlers::handle_rename)? |
527 | .on::<lsp_types::request::References>(handlers::handle_references)? | 527 | .on::<lsp_types::request::References>(handlers::handle_references)? |
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index 6acf80c58..3ff779702 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs | |||
@@ -12,13 +12,14 @@ use lsp_types::{ | |||
12 | CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, | 12 | CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, |
13 | CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, | 13 | CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, |
14 | CodeLens, Command, CompletionItem, Diagnostic, DocumentFormattingParams, DocumentHighlight, | 14 | CodeLens, Command, CompletionItem, Diagnostic, DocumentFormattingParams, DocumentHighlight, |
15 | DocumentSymbol, FoldingRange, FoldingRangeParams, Hover, HoverContents, Location, | 15 | DocumentSymbol, FoldingRange, FoldingRangeParams, HoverContents, Location, MarkupContent, |
16 | MarkupContent, MarkupKind, Position, PrepareRenameResponse, Range, RenameParams, | 16 | MarkupKind, Position, PrepareRenameResponse, Range, RenameParams, SemanticTokensParams, |
17 | SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, | 17 | SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, |
18 | SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit, | 18 | TextDocumentIdentifier, Url, WorkspaceEdit, |
19 | }; | 19 | }; |
20 | use ra_ide::{ | 20 | use ra_ide::{ |
21 | FileId, FilePosition, FileRange, Query, RangeInfo, RunnableKind, SearchScope, TextEdit, | 21 | FileId, FilePosition, FileRange, HoverAction, Query, RangeInfo, RunnableKind, SearchScope, |
22 | TextEdit, | ||
22 | }; | 23 | }; |
23 | use ra_prof::profile; | 24 | use ra_prof::profile; |
24 | use ra_project_model::TargetKind; | 25 | use ra_project_model::TargetKind; |
@@ -537,7 +538,7 @@ pub fn handle_signature_help( | |||
537 | pub fn handle_hover( | 538 | pub fn handle_hover( |
538 | snap: GlobalStateSnapshot, | 539 | snap: GlobalStateSnapshot, |
539 | params: lsp_types::HoverParams, | 540 | params: lsp_types::HoverParams, |
540 | ) -> Result<Option<Hover>> { | 541 | ) -> Result<Option<lsp_ext::Hover>> { |
541 | let _p = profile("handle_hover"); | 542 | let _p = profile("handle_hover"); |
542 | let position = from_proto::file_position(&snap, params.text_document_position_params)?; | 543 | let position = from_proto::file_position(&snap, params.text_document_position_params)?; |
543 | let info = match snap.analysis().hover(position)? { | 544 | let info = match snap.analysis().hover(position)? { |
@@ -546,14 +547,18 @@ pub fn handle_hover( | |||
546 | }; | 547 | }; |
547 | let line_index = snap.analysis.file_line_index(position.file_id)?; | 548 | let line_index = snap.analysis.file_line_index(position.file_id)?; |
548 | let range = to_proto::range(&line_index, info.range); | 549 | let range = to_proto::range(&line_index, info.range); |
549 | let res = Hover { | 550 | let hover = lsp_ext::Hover { |
550 | contents: HoverContents::Markup(MarkupContent { | 551 | hover: lsp_types::Hover { |
551 | kind: MarkupKind::Markdown, | 552 | contents: HoverContents::Markup(MarkupContent { |
552 | value: crate::markdown::format_docs(&info.info.to_markup()), | 553 | kind: MarkupKind::Markdown, |
553 | }), | 554 | value: crate::markdown::format_docs(&info.info.to_markup()), |
554 | range: Some(range), | 555 | }), |
556 | range: Some(range), | ||
557 | }, | ||
558 | actions: prepare_hover_actions(&snap, info.info.actions()), | ||
555 | }; | 559 | }; |
556 | Ok(Some(res)) | 560 | |
561 | Ok(Some(hover)) | ||
557 | } | 562 | } |
558 | 563 | ||
559 | pub fn handle_prepare_rename( | 564 | pub fn handle_prepare_rename( |
@@ -924,24 +929,13 @@ pub fn handle_code_lens_resolve( | |||
924 | _ => vec![], | 929 | _ => vec![], |
925 | }; | 930 | }; |
926 | 931 | ||
927 | let title = if locations.len() == 1 { | 932 | let title = implementation_title(locations.len()); |
928 | "1 implementation".into() | 933 | let cmd = show_references_command( |
929 | } else { | ||
930 | format!("{} implementations", locations.len()) | ||
931 | }; | ||
932 | |||
933 | // We cannot use the 'editor.action.showReferences' command directly | ||
934 | // because that command requires vscode types which we convert in the handler | ||
935 | // on the client side. | ||
936 | let cmd = Command { | ||
937 | title, | 934 | title, |
938 | command: "rust-analyzer.showReferences".into(), | 935 | &lens_params.text_document_position_params.text_document.uri, |
939 | arguments: Some(vec![ | 936 | code_lens.range.start, |
940 | to_value(&lens_params.text_document_position_params.text_document.uri).unwrap(), | 937 | locations, |
941 | to_value(code_lens.range.start).unwrap(), | 938 | ); |
942 | to_value(locations).unwrap(), | ||
943 | ]), | ||
944 | }; | ||
945 | Ok(CodeLens { range: code_lens.range, command: Some(cmd), data: None }) | 939 | Ok(CodeLens { range: code_lens.range, command: Some(cmd), data: None }) |
946 | } | 940 | } |
947 | None => Ok(CodeLens { | 941 | None => Ok(CodeLens { |
@@ -1145,3 +1139,78 @@ pub fn handle_semantic_tokens_range( | |||
1145 | let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); | 1139 | let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); |
1146 | Ok(Some(semantic_tokens.into())) | 1140 | Ok(Some(semantic_tokens.into())) |
1147 | } | 1141 | } |
1142 | |||
1143 | fn implementation_title(count: usize) -> String { | ||
1144 | if count == 1 { | ||
1145 | "1 implementation".into() | ||
1146 | } else { | ||
1147 | format!("{} implementations", count) | ||
1148 | } | ||
1149 | } | ||
1150 | |||
1151 | fn show_references_command( | ||
1152 | title: String, | ||
1153 | uri: &lsp_types::Url, | ||
1154 | position: lsp_types::Position, | ||
1155 | locations: Vec<lsp_types::Location>, | ||
1156 | ) -> Command { | ||
1157 | // We cannot use the 'editor.action.showReferences' command directly | ||
1158 | // because that command requires vscode types which we convert in the handler | ||
1159 | // on the client side. | ||
1160 | |||
1161 | Command { | ||
1162 | title, | ||
1163 | command: "rust-analyzer.showReferences".into(), | ||
1164 | arguments: Some(vec![ | ||
1165 | to_value(uri).unwrap(), | ||
1166 | to_value(position).unwrap(), | ||
1167 | to_value(locations).unwrap(), | ||
1168 | ]), | ||
1169 | } | ||
1170 | } | ||
1171 | |||
1172 | fn to_command_link(command: Command, tooltip: String) -> lsp_ext::CommandLink { | ||
1173 | lsp_ext::CommandLink { tooltip: Some(tooltip), command } | ||
1174 | } | ||
1175 | |||
1176 | fn show_impl_command_link( | ||
1177 | snap: &GlobalStateSnapshot, | ||
1178 | position: &FilePosition, | ||
1179 | ) -> Option<lsp_ext::CommandLinkGroup> { | ||
1180 | if snap.config.hover.implementations { | ||
1181 | if let Some(nav_data) = snap.analysis().goto_implementation(*position).unwrap_or(None) { | ||
1182 | let uri = to_proto::url(snap, position.file_id).ok()?; | ||
1183 | let line_index = snap.analysis().file_line_index(position.file_id).ok()?; | ||
1184 | let position = to_proto::position(&line_index, position.offset); | ||
1185 | let locations: Vec<_> = nav_data | ||
1186 | .info | ||
1187 | .iter() | ||
1188 | .filter_map(|it| to_proto::location(snap, it.file_range()).ok()) | ||
1189 | .collect(); | ||
1190 | let title = implementation_title(locations.len()); | ||
1191 | let command = show_references_command(title, &uri, position, locations); | ||
1192 | |||
1193 | return Some(lsp_ext::CommandLinkGroup { | ||
1194 | commands: vec![to_command_link(command, "Go to implementations".into())], | ||
1195 | ..Default::default() | ||
1196 | }); | ||
1197 | } | ||
1198 | } | ||
1199 | None | ||
1200 | } | ||
1201 | |||
1202 | fn prepare_hover_actions( | ||
1203 | snap: &GlobalStateSnapshot, | ||
1204 | actions: &[HoverAction], | ||
1205 | ) -> Vec<lsp_ext::CommandLinkGroup> { | ||
1206 | if snap.config.hover.none() || !snap.config.client_caps.hover_actions { | ||
1207 | return Vec::new(); | ||
1208 | } | ||
1209 | |||
1210 | actions | ||
1211 | .iter() | ||
1212 | .filter_map(|it| match it { | ||
1213 | HoverAction::Implementaion(position) => show_impl_command_link(snap, position), | ||
1214 | }) | ||
1215 | .collect() | ||
1216 | } | ||
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index 7f7940d0b..a0847dad3 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md | |||
@@ -467,3 +467,41 @@ interface InlayHint { | |||
467 | label: string, | 467 | label: string, |
468 | } | 468 | } |
469 | ``` | 469 | ``` |
470 | |||
471 | ## Hover Actions | ||
472 | |||
473 | **Client Capability:** `{ "hoverActions": boolean }` | ||
474 | |||
475 | If this capability is set, `Hover` request returned from the server might contain an additional field, `actions`: | ||
476 | |||
477 | ```typescript | ||
478 | interface Hover { | ||
479 | ... | ||
480 | actions?: CommandLinkGroup[]; | ||
481 | } | ||
482 | |||
483 | interface CommandLink extends Command { | ||
484 | /** | ||
485 | * A tooltip for the command, when represented in the UI. | ||
486 | */ | ||
487 | tooltip?: string; | ||
488 | } | ||
489 | |||
490 | interface CommandLinkGroup { | ||
491 | title?: string; | ||
492 | commands: CommandLink[]; | ||
493 | } | ||
494 | ``` | ||
495 | |||
496 | Such actions on the client side are appended to a hover bottom as command links: | ||
497 | ``` | ||
498 | +-----------------------------+ | ||
499 | | Hover content | | ||
500 | | | | ||
501 | +-----------------------------+ | ||
502 | | _Action1_ | _Action2_ | <- first group, no TITLE | ||
503 | +-----------------------------+ | ||
504 | | TITLE _Action1_ | _Action2_ | <- second group | ||
505 | +-----------------------------+ | ||
506 | ... | ||
507 | ``` \ No newline at end of file | ||
diff --git a/editors/code/package.json b/editors/code/package.json index 30ab7ba4a..b9c57db3b 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -462,17 +462,27 @@ | |||
462 | "default": true | 462 | "default": true |
463 | }, | 463 | }, |
464 | "rust-analyzer.lens.run": { | 464 | "rust-analyzer.lens.run": { |
465 | "markdownDescription": "Whether to show Run lens. Only applies when `#rust-analyzer.lens.enable#` is set.", | 465 | "markdownDescription": "Whether to show `Run` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", |
466 | "type": "boolean", | 466 | "type": "boolean", |
467 | "default": true | 467 | "default": true |
468 | }, | 468 | }, |
469 | "rust-analyzer.lens.debug": { | 469 | "rust-analyzer.lens.debug": { |
470 | "markdownDescription": "Whether to show Debug lens. Only applies when `#rust-analyzer.lens.enable#` is set.", | 470 | "markdownDescription": "Whether to show `Debug` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", |
471 | "type": "boolean", | 471 | "type": "boolean", |
472 | "default": true | 472 | "default": true |
473 | }, | 473 | }, |
474 | "rust-analyzer.lens.implementations": { | 474 | "rust-analyzer.lens.implementations": { |
475 | "markdownDescription": "Whether to show Implementations lens. Only applies when `#rust-analyzer.lens.enable#` is set.", | 475 | "markdownDescription": "Whether to show `Implementations` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", |
476 | "type": "boolean", | ||
477 | "default": true | ||
478 | }, | ||
479 | "rust-analyzer.hoverActions.enable": { | ||
480 | "description": "Whether to show HoverActions in Rust files.", | ||
481 | "type": "boolean", | ||
482 | "default": true | ||
483 | }, | ||
484 | "rust-analyzer.hoverActions.implementations": { | ||
485 | "markdownDescription": "Whether to show `Implementations` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.", | ||
476 | "type": "boolean", | 486 | "type": "boolean", |
477 | "default": true | 487 | "default": true |
478 | }, | 488 | }, |
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index 40ad1e3cd..65ad573d8 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts | |||
@@ -7,6 +7,20 @@ import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.pr | |||
7 | import { SemanticTokensFeature, DocumentSemanticsTokensSignature } from 'vscode-languageclient/lib/semanticTokens.proposed'; | 7 | import { SemanticTokensFeature, DocumentSemanticsTokensSignature } from 'vscode-languageclient/lib/semanticTokens.proposed'; |
8 | import { assert } from './util'; | 8 | import { assert } from './util'; |
9 | 9 | ||
10 | function renderCommand(cmd: ra.CommandLink) { | ||
11 | return `[${cmd.title}](command:${cmd.command}?${encodeURIComponent(JSON.stringify(cmd.arguments))} '${cmd.tooltip!}')`; | ||
12 | } | ||
13 | |||
14 | function renderHoverActions(actions: ra.CommandLinkGroup[]): vscode.MarkdownString { | ||
15 | const text = actions.map(group => | ||
16 | (group.title ? (group.title + " ") : "") + group.commands.map(renderCommand).join(' | ') | ||
17 | ).join('___'); | ||
18 | |||
19 | const result = new vscode.MarkdownString(text); | ||
20 | result.isTrusted = true; | ||
21 | return result; | ||
22 | } | ||
23 | |||
10 | export function createClient(serverPath: string, cwd: string): lc.LanguageClient { | 24 | export function createClient(serverPath: string, cwd: string): lc.LanguageClient { |
11 | // '.' Is the fallback if no folder is open | 25 | // '.' Is the fallback if no folder is open |
12 | // TODO?: Workspace folders support Uri's (eg: file://test.txt). | 26 | // TODO?: Workspace folders support Uri's (eg: file://test.txt). |
@@ -35,6 +49,23 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient | |||
35 | if (res === undefined) throw new Error('busy'); | 49 | if (res === undefined) throw new Error('busy'); |
36 | return res; | 50 | return res; |
37 | }, | 51 | }, |
52 | async provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, _next: lc.ProvideHoverSignature) { | ||
53 | return client.sendRequest(lc.HoverRequest.type, client.code2ProtocolConverter.asTextDocumentPositionParams(document, position), token).then( | ||
54 | (result) => { | ||
55 | const hover = client.protocol2CodeConverter.asHover(result); | ||
56 | if (hover) { | ||
57 | const actions = (<any>result).actions; | ||
58 | if (actions) { | ||
59 | hover.contents.push(renderHoverActions(actions)); | ||
60 | } | ||
61 | } | ||
62 | return hover; | ||
63 | }, | ||
64 | (error) => { | ||
65 | client.logFailedRequest(lc.HoverRequest.type, error); | ||
66 | return Promise.resolve(null); | ||
67 | }); | ||
68 | }, | ||
38 | // Using custom handling of CodeActions where each code action is resloved lazily | 69 | // Using custom handling of CodeActions where each code action is resloved lazily |
39 | // That's why we are not waiting for any command or edits | 70 | // That's why we are not waiting for any command or edits |
40 | async provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken, _next: lc.ProvideCodeActionsSignature) { | 71 | async provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken, _next: lc.ProvideCodeActionsSignature) { |
@@ -129,6 +160,7 @@ class ExperimentalFeatures implements lc.StaticFeature { | |||
129 | caps.snippetTextEdit = true; | 160 | caps.snippetTextEdit = true; |
130 | caps.codeActionGroup = true; | 161 | caps.codeActionGroup = true; |
131 | caps.resolveCodeAction = true; | 162 | caps.resolveCodeAction = true; |
163 | caps.hoverActions = true; | ||
132 | capabilities.experimental = caps; | 164 | capabilities.experimental = caps; |
133 | } | 165 | } |
134 | initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void { | 166 | initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void { |
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index e8abf8284..d8f0037d4 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts | |||
@@ -16,10 +16,8 @@ export class Config { | |||
16 | "files", | 16 | "files", |
17 | "highlighting", | 17 | "highlighting", |
18 | "updates.channel", | 18 | "updates.channel", |
19 | "lens.enable", | 19 | "lens", // works as lens.* |
20 | "lens.run", | 20 | "hoverActions", // works as hoverActions.* |
21 | "lens.debug", | ||
22 | "lens.implementations", | ||
23 | ] | 21 | ] |
24 | .map(opt => `${this.rootSection}.${opt}`); | 22 | .map(opt => `${this.rootSection}.${opt}`); |
25 | 23 | ||
@@ -132,4 +130,11 @@ export class Config { | |||
132 | implementations: this.get<boolean>("lens.implementations"), | 130 | implementations: this.get<boolean>("lens.implementations"), |
133 | }; | 131 | }; |
134 | } | 132 | } |
133 | |||
134 | get hoverActions() { | ||
135 | return { | ||
136 | enable: this.get<boolean>("hoverActions.enable"), | ||
137 | implementations: this.get<boolean>("hoverActions.implementations"), | ||
138 | }; | ||
139 | } | ||
135 | } | 140 | } |
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index 9793b926c..e16ea799c 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts | |||
@@ -90,3 +90,15 @@ export interface SsrParams { | |||
90 | parseOnly: boolean; | 90 | parseOnly: boolean; |
91 | } | 91 | } |
92 | export const ssr = new lc.RequestType<SsrParams, lc.WorkspaceEdit, void>('experimental/ssr'); | 92 | export const ssr = new lc.RequestType<SsrParams, lc.WorkspaceEdit, void>('experimental/ssr'); |
93 | |||
94 | export interface CommandLink extends lc.Command { | ||
95 | /** | ||
96 | * A tooltip for the command, when represented in the UI. | ||
97 | */ | ||
98 | tooltip?: string; | ||
99 | } | ||
100 | |||
101 | export interface CommandLinkGroup { | ||
102 | title?: string; | ||
103 | commands: CommandLink[]; | ||
104 | } | ||