diff options
-rw-r--r-- | crates/ra_hir_ty/src/lib.rs | 12 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/lower.rs | 42 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests/traits.rs | 30 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/utils.rs | 48 | ||||
-rw-r--r-- | docs/user/readme.adoc | 27 |
5 files changed, 140 insertions, 19 deletions
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index 279c06d65..a8ef32ec5 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs | |||
@@ -487,6 +487,18 @@ impl<T> Binders<T> { | |||
487 | pub fn new(num_binders: usize, value: T) -> Self { | 487 | pub fn new(num_binders: usize, value: T) -> Self { |
488 | Self { num_binders, value } | 488 | Self { num_binders, value } |
489 | } | 489 | } |
490 | |||
491 | pub fn as_ref(&self) -> Binders<&T> { | ||
492 | Binders { num_binders: self.num_binders, value: &self.value } | ||
493 | } | ||
494 | |||
495 | pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Binders<U> { | ||
496 | Binders { num_binders: self.num_binders, value: f(self.value) } | ||
497 | } | ||
498 | |||
499 | pub fn filter_map<U>(self, f: impl FnOnce(T) -> Option<U>) -> Option<Binders<U>> { | ||
500 | Some(Binders { num_binders: self.num_binders, value: f(self.value)? }) | ||
501 | } | ||
490 | } | 502 | } |
491 | 503 | ||
492 | impl<T: Clone> Binders<&T> { | 504 | impl<T: Clone> Binders<&T> { |
diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs index b57214296..a6f893037 100644 --- a/crates/ra_hir_ty/src/lower.rs +++ b/crates/ra_hir_ty/src/lower.rs | |||
@@ -28,11 +28,11 @@ use crate::{ | |||
28 | db::HirDatabase, | 28 | db::HirDatabase, |
29 | primitive::{FloatTy, IntTy}, | 29 | primitive::{FloatTy, IntTy}, |
30 | utils::{ | 30 | utils::{ |
31 | all_super_traits, associated_type_by_name_including_super_traits, generics, make_mut_slice, | 31 | all_super_trait_refs, associated_type_by_name_including_super_traits, generics, |
32 | variant_data, | 32 | make_mut_slice, variant_data, |
33 | }, | 33 | }, |
34 | Binders, BoundVar, DebruijnIndex, FnSig, GenericPredicate, PolyFnSig, ProjectionPredicate, | 34 | Binders, BoundVar, DebruijnIndex, FnSig, GenericPredicate, PolyFnSig, ProjectionPredicate, |
35 | ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, | 35 | ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk, |
36 | }; | 36 | }; |
37 | 37 | ||
38 | #[derive(Debug)] | 38 | #[derive(Debug)] |
@@ -256,7 +256,7 @@ impl Ty { | |||
256 | if remaining_segments.len() == 1 { | 256 | if remaining_segments.len() == 1 { |
257 | // resolve unselected assoc types | 257 | // resolve unselected assoc types |
258 | let segment = remaining_segments.first().unwrap(); | 258 | let segment = remaining_segments.first().unwrap(); |
259 | (Ty::select_associated_type(ctx, ty, res, segment), None) | 259 | (Ty::select_associated_type(ctx, res, segment), None) |
260 | } else if remaining_segments.len() > 1 { | 260 | } else if remaining_segments.len() > 1 { |
261 | // FIXME report error (ambiguous associated type) | 261 | // FIXME report error (ambiguous associated type) |
262 | (Ty::Unknown, None) | 262 | (Ty::Unknown, None) |
@@ -380,21 +380,20 @@ impl Ty { | |||
380 | 380 | ||
381 | fn select_associated_type( | 381 | fn select_associated_type( |
382 | ctx: &TyLoweringContext<'_>, | 382 | ctx: &TyLoweringContext<'_>, |
383 | self_ty: Ty, | ||
384 | res: Option<TypeNs>, | 383 | res: Option<TypeNs>, |
385 | segment: PathSegment<'_>, | 384 | segment: PathSegment<'_>, |
386 | ) -> Ty { | 385 | ) -> Ty { |
387 | let traits_from_env: Vec<_> = match res { | 386 | let traits_from_env: Vec<_> = match res { |
388 | Some(TypeNs::SelfType(impl_id)) => match ctx.db.impl_trait(impl_id) { | 387 | Some(TypeNs::SelfType(impl_id)) => match ctx.db.impl_trait(impl_id) { |
389 | None => return Ty::Unknown, | 388 | None => return Ty::Unknown, |
390 | Some(trait_ref) => vec![trait_ref.value.trait_], | 389 | Some(trait_ref) => vec![trait_ref.value], |
391 | }, | 390 | }, |
392 | Some(TypeNs::GenericParam(param_id)) => { | 391 | Some(TypeNs::GenericParam(param_id)) => { |
393 | let predicates = ctx.db.generic_predicates_for_param(param_id); | 392 | let predicates = ctx.db.generic_predicates_for_param(param_id); |
394 | let mut traits_: Vec<_> = predicates | 393 | let mut traits_: Vec<_> = predicates |
395 | .iter() | 394 | .iter() |
396 | .filter_map(|pred| match &pred.value { | 395 | .filter_map(|pred| match &pred.value { |
397 | GenericPredicate::Implemented(tr) => Some(tr.trait_), | 396 | GenericPredicate::Implemented(tr) => Some(tr.clone()), |
398 | _ => None, | 397 | _ => None, |
399 | }) | 398 | }) |
400 | .collect(); | 399 | .collect(); |
@@ -404,20 +403,37 @@ impl Ty { | |||
404 | if generics.params.types[param_id.local_id].provenance | 403 | if generics.params.types[param_id.local_id].provenance |
405 | == TypeParamProvenance::TraitSelf | 404 | == TypeParamProvenance::TraitSelf |
406 | { | 405 | { |
407 | traits_.push(trait_id); | 406 | let trait_ref = TraitRef { |
407 | trait_: trait_id, | ||
408 | substs: Substs::bound_vars(&generics, DebruijnIndex::INNERMOST), | ||
409 | }; | ||
410 | traits_.push(trait_ref); | ||
408 | } | 411 | } |
409 | } | 412 | } |
410 | traits_ | 413 | traits_ |
411 | } | 414 | } |
412 | _ => return Ty::Unknown, | 415 | _ => return Ty::Unknown, |
413 | }; | 416 | }; |
414 | let traits = traits_from_env.into_iter().flat_map(|t| all_super_traits(ctx.db.upcast(), t)); | 417 | let traits = traits_from_env.into_iter().flat_map(|t| all_super_trait_refs(ctx.db, t)); |
415 | for t in traits { | 418 | for t in traits { |
416 | if let Some(associated_ty) = ctx.db.trait_data(t).associated_type_by_name(&segment.name) | 419 | if let Some(associated_ty) = |
420 | ctx.db.trait_data(t.trait_).associated_type_by_name(&segment.name) | ||
417 | { | 421 | { |
418 | let substs = | 422 | let substs = match ctx.type_param_mode { |
419 | Substs::build_for_def(ctx.db, t).push(self_ty).fill_with_unknown().build(); | 423 | TypeParamLoweringMode::Placeholder => { |
420 | // FIXME handle type parameters on the segment | 424 | // if we're lowering to placeholders, we have to put |
425 | // them in now | ||
426 | let s = Substs::type_params( | ||
427 | ctx.db, | ||
428 | ctx.resolver | ||
429 | .generic_def() | ||
430 | .expect("there should be generics if there's a generic param"), | ||
431 | ); | ||
432 | t.substs.subst_bound_vars(&s) | ||
433 | } | ||
434 | TypeParamLoweringMode::Variable => t.substs, | ||
435 | }; | ||
436 | // FIXME handle (forbid) type parameters on the segment | ||
421 | return Ty::Projection(ProjectionTy { associated_ty, parameters: substs }); | 437 | return Ty::Projection(ProjectionTy { associated_ty, parameters: substs }); |
422 | } | 438 | } |
423 | } | 439 | } |
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index f51cdd496..e555c879a 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs | |||
@@ -1898,6 +1898,36 @@ fn test() { | |||
1898 | } | 1898 | } |
1899 | 1899 | ||
1900 | #[test] | 1900 | #[test] |
1901 | fn unselected_projection_chalk_fold() { | ||
1902 | let t = type_at( | ||
1903 | r#" | ||
1904 | //- /main.rs | ||
1905 | trait Interner {} | ||
1906 | trait Fold<I: Interner, TI = I> { | ||
1907 | type Result; | ||
1908 | } | ||
1909 | |||
1910 | struct Ty<I: Interner> {} | ||
1911 | impl<I: Interner, TI: Interner> Fold<I, TI> for Ty<I> { | ||
1912 | type Result = Ty<TI>; | ||
1913 | } | ||
1914 | |||
1915 | fn fold<I: Interner, T>(interner: &I, t: T) -> T::Result | ||
1916 | where | ||
1917 | T: Fold<I, I>, | ||
1918 | { | ||
1919 | loop {} | ||
1920 | } | ||
1921 | |||
1922 | fn foo<I: Interner>(interner: &I, t: Ty<I>) { | ||
1923 | fold(interner, t)<|>; | ||
1924 | } | ||
1925 | "#, | ||
1926 | ); | ||
1927 | assert_eq!(t, "Ty<I>"); | ||
1928 | } | ||
1929 | |||
1930 | #[test] | ||
1901 | fn trait_impl_self_ty() { | 1931 | fn trait_impl_self_ty() { |
1902 | let t = type_at( | 1932 | let t = type_at( |
1903 | r#" | 1933 | r#" |
diff --git a/crates/ra_hir_ty/src/utils.rs b/crates/ra_hir_ty/src/utils.rs index 1e5022fa4..f98350bf9 100644 --- a/crates/ra_hir_ty/src/utils.rs +++ b/crates/ra_hir_ty/src/utils.rs | |||
@@ -14,6 +14,8 @@ use hir_def::{ | |||
14 | }; | 14 | }; |
15 | use hir_expand::name::{name, Name}; | 15 | use hir_expand::name::{name, Name}; |
16 | 16 | ||
17 | use crate::{db::HirDatabase, GenericPredicate, TraitRef}; | ||
18 | |||
17 | fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> { | 19 | fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> { |
18 | let resolver = trait_.resolver(db); | 20 | let resolver = trait_.resolver(db); |
19 | // returning the iterator directly doesn't easily work because of | 21 | // returning the iterator directly doesn't easily work because of |
@@ -41,6 +43,28 @@ fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> { | |||
41 | .collect() | 43 | .collect() |
42 | } | 44 | } |
43 | 45 | ||
46 | fn direct_super_trait_refs(db: &dyn HirDatabase, trait_ref: &TraitRef) -> Vec<TraitRef> { | ||
47 | // returning the iterator directly doesn't easily work because of | ||
48 | // lifetime problems, but since there usually shouldn't be more than a | ||
49 | // few direct traits this should be fine (we could even use some kind of | ||
50 | // SmallVec if performance is a concern) | ||
51 | let generic_params = db.generic_params(trait_ref.trait_.into()); | ||
52 | let trait_self = match generic_params.find_trait_self_param() { | ||
53 | Some(p) => TypeParamId { parent: trait_ref.trait_.into(), local_id: p }, | ||
54 | None => return Vec::new(), | ||
55 | }; | ||
56 | db.generic_predicates_for_param(trait_self) | ||
57 | .iter() | ||
58 | .filter_map(|pred| { | ||
59 | pred.as_ref().filter_map(|pred| match pred { | ||
60 | GenericPredicate::Implemented(tr) => Some(tr.clone()), | ||
61 | _ => None, | ||
62 | }) | ||
63 | }) | ||
64 | .map(|pred| pred.subst(&trait_ref.substs)) | ||
65 | .collect() | ||
66 | } | ||
67 | |||
44 | /// Returns an iterator over the whole super trait hierarchy (including the | 68 | /// Returns an iterator over the whole super trait hierarchy (including the |
45 | /// trait itself). | 69 | /// trait itself). |
46 | pub(super) fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> { | 70 | pub(super) fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> { |
@@ -62,6 +86,30 @@ pub(super) fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<Tra | |||
62 | result | 86 | result |
63 | } | 87 | } |
64 | 88 | ||
89 | /// Given a trait ref (`Self: Trait`), builds all the implied trait refs for | ||
90 | /// super traits. The original trait ref will be included. So the difference to | ||
91 | /// `all_super_traits` is that we keep track of type parameters; for example if | ||
92 | /// we have `Self: Trait<u32, i32>` and `Trait<T, U>: OtherTrait<U>` we'll get | ||
93 | /// `Self: OtherTrait<i32>`. | ||
94 | pub(super) fn all_super_trait_refs(db: &dyn HirDatabase, trait_ref: TraitRef) -> Vec<TraitRef> { | ||
95 | // we need to take care a bit here to avoid infinite loops in case of cycles | ||
96 | // (i.e. if we have `trait A: B; trait B: A;`) | ||
97 | let mut result = vec![trait_ref]; | ||
98 | let mut i = 0; | ||
99 | while i < result.len() { | ||
100 | let t = &result[i]; | ||
101 | // yeah this is quadratic, but trait hierarchies should be flat | ||
102 | // enough that this doesn't matter | ||
103 | for tt in direct_super_trait_refs(db, t) { | ||
104 | if !result.iter().any(|tr| tr.trait_ == tt.trait_) { | ||
105 | result.push(tt); | ||
106 | } | ||
107 | } | ||
108 | i += 1; | ||
109 | } | ||
110 | result | ||
111 | } | ||
112 | |||
65 | /// Finds a path from a trait to one of its super traits. Returns an empty | 113 | /// Finds a path from a trait to one of its super traits. Returns an empty |
66 | /// vector if there is no path. | 114 | /// vector if there is no path. |
67 | pub(super) fn find_super_trait_path( | 115 | pub(super) fn find_super_trait_path( |
diff --git a/docs/user/readme.adoc b/docs/user/readme.adoc index ce5704836..76d065d35 100644 --- a/docs/user/readme.adoc +++ b/docs/user/readme.adoc | |||
@@ -23,7 +23,7 @@ https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/readme.adoc | |||
23 | 23 | ||
24 | == Installation | 24 | == Installation |
25 | 25 | ||
26 | In theory, one should be able to just install the server binary and have it automatically work with any editor. | 26 | In theory, one should be able to just install the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>> and have it automatically work with any editor. |
27 | We are not there yet, so some editor specific setup is required. | 27 | We are not there yet, so some editor specific setup is required. |
28 | 28 | ||
29 | Additionally, rust-analyzer needs the sources of the standard library. | 29 | Additionally, rust-analyzer needs the sources of the standard library. |
@@ -108,15 +108,26 @@ Here are some useful self-diagnostic commands: | |||
108 | * To log all LSP requests, add `"rust-analyzer.trace.server": "verbose"` to the settings and look for `Server Trace` in the panel. | 108 | * To log all LSP requests, add `"rust-analyzer.trace.server": "verbose"` to the settings and look for `Server Trace` in the panel. |
109 | * To enable client-side logging, add `"rust-analyzer.trace.extension": true` to the settings and open the `Console` tab of VS Code developer tools. | 109 | * To enable client-side logging, add `"rust-analyzer.trace.extension": true` to the settings and open the `Console` tab of VS Code developer tools. |
110 | 110 | ||
111 | === Language Server Binary | 111 | === rust-analyzer Language Server Binary |
112 | 112 | ||
113 | Other editors generally require the `rust-analyzer` binary to be in `$PATH`. | 113 | Other editors generally require the `rust-analyzer` binary to be in `$PATH`. |
114 | You can download the pre-built binary from the https://github.com/rust-analyzer/rust-analyzer/releases[releases] page. Typically, you then need to rename the binary for your platform, e.g. `rust-analyzer-mac` if you're on Mac OS, to `rust-analzyer` and make it executable in addition to moving it into a directory in your `$PATH`. | 114 | You can download the pre-built binary from the https://github.com/rust-analyzer/rust-analyzer/releases[releases] page. Typically, you then need to rename the binary for your platform, e.g. `rust-analyzer-mac` if you're on Mac OS, to `rust-analzyer` and make it executable in addition to moving it into a directory in your `$PATH`. |
115 | 115 | ||
116 | On Linux to install the `rust-analyzer` binary into `~/.local/bin`, this commands could be used | ||
117 | |||
118 | [source,bash] | ||
119 | ---- | ||
120 | $ curl -L https://github.com/rust-analyzer/rust-analyzer/releases/latest/download/rust-analyzer-linux -o ~/.local/bin/rust-analyzer | ||
121 | $ chmod +x ~/.local/bin/rust-analyzer | ||
122 | ---- | ||
123 | |||
124 | Ensure `~/.local/bin` is listed in the `$PATH` variable. | ||
125 | |||
116 | Alternatively, you can install it from source using the following command: | 126 | Alternatively, you can install it from source using the following command: |
117 | 127 | ||
118 | [source,bash] | 128 | [source,bash] |
119 | ---- | 129 | ---- |
130 | $ git clone https://github.com/rust-analyzer/rust-analyzer.git && cd rust-analyzer | ||
120 | $ cargo xtask install --server | 131 | $ cargo xtask install --server |
121 | ---- | 132 | ---- |
122 | 133 | ||
@@ -139,15 +150,19 @@ $ yay -S rust-analyzer-bin | |||
139 | 150 | ||
140 | === Emacs | 151 | === Emacs |
141 | 152 | ||
142 | Emacs support is maintained https://github.com/emacs-lsp/lsp-mode/blob/master/lsp-rust.el[upstream]. | 153 | Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>. |
143 | 154 | ||
144 | 1. Install the most recent version of `emacs-lsp` package by following the instructions https://github.com/emacs-lsp/lsp-mode[here]. | 155 | Emacs support is maintained as part of the https://github.com/emacs-lsp/lsp-mode[Emacs-LSP] package in https://github.com/emacs-lsp/lsp-mode/blob/master/lsp-rust.el[lsp-rust.el]. |
156 | |||
157 | 1. Install the most recent version of `emacs-lsp` package by following the https://github.com/emacs-lsp/lsp-mode[Emacs-LSP instructions]. | ||
145 | 2. Set `lsp-rust-server` to `'rust-analyzer`. | 158 | 2. Set `lsp-rust-server` to `'rust-analyzer`. |
146 | 3. Run `lsp` in a Rust buffer. | 159 | 3. Run `lsp` in a Rust buffer. |
147 | 4. (Optionally) bind commands like `lsp-rust-analyzer-join-lines`, `lsp-extend-selection` and `lsp-rust-analyzer-expand-macro` to keys. | 160 | 4. (Optionally) bind commands like `lsp-rust-analyzer-join-lines`, `lsp-extend-selection` and `lsp-rust-analyzer-expand-macro` to keys. |
148 | 161 | ||
149 | === Vim | 162 | === Vim |
150 | 163 | ||
164 | Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>. | ||
165 | |||
151 | The are several LSP client implementations for vim: | 166 | The are several LSP client implementations for vim: |
152 | 167 | ||
153 | ==== coc-rust-analyzer | 168 | ==== coc-rust-analyzer |
@@ -205,7 +220,7 @@ Once `neovim/nvim-lsp` is installed, use `+lua require'nvim_lsp'.rust_analyzer.s | |||
205 | 220 | ||
206 | === Sublime Text 3 | 221 | === Sublime Text 3 |
207 | 222 | ||
208 | Prerequisites: You have installed the <<language-server-binary,`rust-analyzer` binary>>. | 223 | Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>. |
209 | 224 | ||
210 | You also need the `LSP` package. To install it: | 225 | You also need the `LSP` package. To install it: |
211 | 226 | ||
@@ -218,7 +233,7 @@ Finally, with your Rust project open, in the command palette, run `LSP: Enable L | |||
218 | 233 | ||
219 | If it worked, you should see "rust-analzyer, Line X, Column Y" on the left side of the bottom bar, and after waiting a bit, functionality like tooltips on hovering over variables should become available. | 234 | If it worked, you should see "rust-analzyer, Line X, Column Y" on the left side of the bottom bar, and after waiting a bit, functionality like tooltips on hovering over variables should become available. |
220 | 235 | ||
221 | If you get an error saying `No such file or directory: 'rust-analyzer'`, see the <<language-server-binary,section on installing the language server binary>>. | 236 | If you get an error saying `No such file or directory: 'rust-analyzer'`, see the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>> section on installing the language server binary. |
222 | 237 | ||
223 | == Usage | 238 | == Usage |
224 | 239 | ||