diff options
author | oxalica <[email protected]> | 2021-03-14 12:03:39 +0000 |
---|---|---|
committer | oxalica <[email protected]> | 2021-03-15 17:04:20 +0000 |
commit | ef416e0154767619fcbfa0d1682b28bd338a8ce9 (patch) | |
tree | e73269dbb569f176e9bba1b16eb74731d63c94c2 /crates/hir/src | |
parent | 2bb8956a102cb2efbea35e414a8214fba2efcaf6 (diff) |
Impl HirDisplay for function hover message
Diffstat (limited to 'crates/hir/src')
-rw-r--r-- | crates/hir/src/display.rs | 234 | ||||
-rw-r--r-- | crates/hir/src/lib.rs | 17 |
2 files changed, 247 insertions, 4 deletions
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index 86f48256e..ae08e2584 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs | |||
@@ -1,9 +1,120 @@ | |||
1 | //! HirDisplay implementations for various hir types. | 1 | //! HirDisplay implementations for various hir types. |
2 | use hir_def::{ | ||
3 | generics::{TypeParamProvenance, WherePredicate, WherePredicateTypeTarget}, | ||
4 | type_ref::{TypeBound, TypeRef}, | ||
5 | GenericDefId, | ||
6 | }; | ||
2 | use hir_ty::display::{ | 7 | use hir_ty::display::{ |
3 | write_bounds_like_dyn_trait_with_prefix, HirDisplay, HirDisplayError, HirFormatter, | 8 | write_bounds_like_dyn_trait_with_prefix, write_visibility, HirDisplay, HirDisplayError, |
9 | HirFormatter, | ||
4 | }; | 10 | }; |
11 | use syntax::ast::{self, NameOwner}; | ||
12 | |||
13 | use crate::{Function, HasVisibility, Substs, Type, TypeParam}; | ||
14 | |||
15 | impl HirDisplay for Function { | ||
16 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { | ||
17 | let data = f.db.function_data(self.id); | ||
18 | write_visibility(self.module(f.db).id, self.visibility(f.db), f)?; | ||
19 | let qual = &data.qualifier; | ||
20 | if qual.is_default { | ||
21 | write!(f, "default ")?; | ||
22 | } | ||
23 | if qual.is_const { | ||
24 | write!(f, "const ")?; | ||
25 | } | ||
26 | if qual.is_async { | ||
27 | write!(f, "async ")?; | ||
28 | } | ||
29 | if qual.is_unsafe { | ||
30 | write!(f, "unsafe ")?; | ||
31 | } | ||
32 | if let Some(abi) = &qual.abi { | ||
33 | // FIXME: String escape? | ||
34 | write!(f, "extern \"{}\" ", abi)?; | ||
35 | } | ||
36 | write!(f, "fn {}", data.name)?; | ||
37 | |||
38 | write_generic_params(GenericDefId::FunctionId(self.id), f)?; | ||
39 | |||
40 | write!(f, "(")?; | ||
5 | 41 | ||
6 | use crate::{Substs, Type, TypeParam}; | 42 | let write_self_param = |ty: &TypeRef, f: &mut HirFormatter| match ty { |
43 | TypeRef::Path(p) if p.is_self_type() => write!(f, "self"), | ||
44 | TypeRef::Reference(inner, lifetime, mut_) if matches!(&**inner,TypeRef::Path(p) if p.is_self_type()) => | ||
45 | { | ||
46 | write!(f, "&")?; | ||
47 | if let Some(lifetime) = lifetime { | ||
48 | write!(f, "{} ", lifetime.name)?; | ||
49 | } | ||
50 | if let hir_def::type_ref::Mutability::Mut = mut_ { | ||
51 | write!(f, "mut ")?; | ||
52 | } | ||
53 | write!(f, "self") | ||
54 | } | ||
55 | _ => { | ||
56 | write!(f, "self: ")?; | ||
57 | ty.hir_fmt(f) | ||
58 | } | ||
59 | }; | ||
60 | |||
61 | let mut first = true; | ||
62 | for (param, type_ref) in self.assoc_fn_params(f.db).into_iter().zip(&data.params) { | ||
63 | if !first { | ||
64 | write!(f, ", ")?; | ||
65 | } else { | ||
66 | first = false; | ||
67 | if data.has_self_param { | ||
68 | write_self_param(type_ref, f)?; | ||
69 | continue; | ||
70 | } | ||
71 | } | ||
72 | match param.pattern_source(f.db) { | ||
73 | Some(ast::Pat::IdentPat(p)) if p.name().is_some() => { | ||
74 | write!(f, "{}: ", p.name().unwrap())? | ||
75 | } | ||
76 | _ => write!(f, "_: ")?, | ||
77 | } | ||
78 | // FIXME: Use resolved `param.ty` or raw `type_ref`? | ||
79 | // The former will ignore lifetime arguments currently. | ||
80 | type_ref.hir_fmt(f)?; | ||
81 | } | ||
82 | write!(f, ")")?; | ||
83 | |||
84 | // `FunctionData::ret_type` will be `::core::future::Future<Output = ...>` for async fns. | ||
85 | // Use ugly pattern match to strip the Future trait. | ||
86 | // Better way? | ||
87 | let ret_type = if !qual.is_async { | ||
88 | &data.ret_type | ||
89 | } else { | ||
90 | match &data.ret_type { | ||
91 | TypeRef::ImplTrait(bounds) => match &bounds[0] { | ||
92 | TypeBound::Path(path) => { | ||
93 | path.segments().iter().last().unwrap().args_and_bindings.unwrap().bindings | ||
94 | [0] | ||
95 | .type_ref | ||
96 | .as_ref() | ||
97 | .unwrap() | ||
98 | } | ||
99 | _ => panic!("Async fn ret_type should be impl Future"), | ||
100 | }, | ||
101 | _ => panic!("Async fn ret_type should be impl Future"), | ||
102 | } | ||
103 | }; | ||
104 | |||
105 | match ret_type { | ||
106 | TypeRef::Tuple(tup) if tup.is_empty() => {} | ||
107 | ty => { | ||
108 | write!(f, " -> ")?; | ||
109 | ty.hir_fmt(f)?; | ||
110 | } | ||
111 | } | ||
112 | |||
113 | write_where_clause(GenericDefId::FunctionId(self.id), f)?; | ||
114 | |||
115 | Ok(()) | ||
116 | } | ||
117 | } | ||
7 | 118 | ||
8 | impl HirDisplay for Type { | 119 | impl HirDisplay for Type { |
9 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { | 120 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
@@ -23,3 +134,122 @@ impl HirDisplay for TypeParam { | |||
23 | Ok(()) | 134 | Ok(()) |
24 | } | 135 | } |
25 | } | 136 | } |
137 | |||
138 | fn write_generic_params(def: GenericDefId, f: &mut HirFormatter) -> Result<(), HirDisplayError> { | ||
139 | let params = f.db.generic_params(def); | ||
140 | if params.lifetimes.is_empty() && params.types.is_empty() && params.consts.is_empty() { | ||
141 | return Ok(()); | ||
142 | } | ||
143 | write!(f, "<")?; | ||
144 | |||
145 | let mut first = true; | ||
146 | let mut delim = |f: &mut HirFormatter| { | ||
147 | if first { | ||
148 | first = false; | ||
149 | Ok(()) | ||
150 | } else { | ||
151 | write!(f, ", ") | ||
152 | } | ||
153 | }; | ||
154 | for (_, lifetime) in params.lifetimes.iter() { | ||
155 | delim(f)?; | ||
156 | write!(f, "{}", lifetime.name)?; | ||
157 | } | ||
158 | for (_, ty) in params.types.iter() { | ||
159 | if ty.provenance != TypeParamProvenance::TypeParamList { | ||
160 | continue; | ||
161 | } | ||
162 | if let Some(name) = &ty.name { | ||
163 | delim(f)?; | ||
164 | write!(f, "{}", name)?; | ||
165 | if let Some(default) = &ty.default { | ||
166 | write!(f, " = ")?; | ||
167 | default.hir_fmt(f)?; | ||
168 | } | ||
169 | } | ||
170 | } | ||
171 | for (_, konst) in params.consts.iter() { | ||
172 | delim(f)?; | ||
173 | write!(f, "const {}: ", konst.name)?; | ||
174 | konst.ty.hir_fmt(f)?; | ||
175 | } | ||
176 | |||
177 | write!(f, ">")?; | ||
178 | Ok(()) | ||
179 | } | ||
180 | |||
181 | fn write_where_clause(def: GenericDefId, f: &mut HirFormatter) -> Result<(), HirDisplayError> { | ||
182 | let params = f.db.generic_params(def); | ||
183 | if params.where_predicates.is_empty() { | ||
184 | return Ok(()); | ||
185 | } | ||
186 | |||
187 | let write_target = |target: &WherePredicateTypeTarget, f: &mut HirFormatter| match target { | ||
188 | WherePredicateTypeTarget::TypeRef(ty) => ty.hir_fmt(f), | ||
189 | WherePredicateTypeTarget::TypeParam(id) => match ¶ms.types[*id].name { | ||
190 | Some(name) => write!(f, "{}", name), | ||
191 | None => write!(f, "{{unnamed}}"), | ||
192 | }, | ||
193 | }; | ||
194 | |||
195 | write!(f, "\nwhere")?; | ||
196 | |||
197 | for (pred_idx, pred) in params.where_predicates.iter().enumerate() { | ||
198 | let prev_pred = | ||
199 | if pred_idx == 0 { None } else { Some(¶ms.where_predicates[pred_idx - 1]) }; | ||
200 | |||
201 | let new_predicate = |f: &mut HirFormatter| { | ||
202 | write!(f, "{}", if pred_idx == 0 { "\n " } else { ",\n " }) | ||
203 | }; | ||
204 | |||
205 | match pred { | ||
206 | WherePredicate::TypeBound { target, bound } => { | ||
207 | if matches!(prev_pred, Some(WherePredicate::TypeBound { target: target_, .. }) if target_ == target) | ||
208 | { | ||
209 | write!(f, " + ")?; | ||
210 | } else { | ||
211 | new_predicate(f)?; | ||
212 | write_target(target, f)?; | ||
213 | write!(f, ": ")?; | ||
214 | } | ||
215 | bound.hir_fmt(f)?; | ||
216 | } | ||
217 | WherePredicate::Lifetime { target, bound } => { | ||
218 | if matches!(prev_pred, Some(WherePredicate::Lifetime { target: target_, .. }) if target_ == target) | ||
219 | { | ||
220 | write!(f, " + {}", bound.name)?; | ||
221 | } else { | ||
222 | new_predicate(f)?; | ||
223 | write!(f, "{}: {}", target.name, bound.name)?; | ||
224 | } | ||
225 | } | ||
226 | WherePredicate::ForLifetime { lifetimes, target, bound } => { | ||
227 | if matches!( | ||
228 | prev_pred, | ||
229 | Some(WherePredicate::ForLifetime { lifetimes: lifetimes_, target: target_, .. }) | ||
230 | if lifetimes_ == lifetimes && target_ == target, | ||
231 | ) { | ||
232 | write!(f, " + ")?; | ||
233 | } else { | ||
234 | new_predicate(f)?; | ||
235 | write!(f, "for<")?; | ||
236 | for (idx, lifetime) in lifetimes.iter().enumerate() { | ||
237 | if idx != 0 { | ||
238 | write!(f, ", ")?; | ||
239 | } | ||
240 | write!(f, "{}", lifetime)?; | ||
241 | } | ||
242 | write!(f, "> ")?; | ||
243 | write_target(target, f)?; | ||
244 | write!(f, ": ")?; | ||
245 | } | ||
246 | bound.hir_fmt(f)?; | ||
247 | } | ||
248 | } | ||
249 | } | ||
250 | |||
251 | // End of final predicate. There must be at least one predicate here. | ||
252 | write!(f, ",")?; | ||
253 | |||
254 | Ok(()) | ||
255 | } | ||
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 0d0e757fc..476fdb132 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -822,7 +822,8 @@ impl Function { | |||
822 | db.function_data(self.id) | 822 | db.function_data(self.id) |
823 | .params | 823 | .params |
824 | .iter() | 824 | .iter() |
825 | .map(|type_ref| { | 825 | .enumerate() |
826 | .map(|(idx, type_ref)| { | ||
826 | let ty = Type { | 827 | let ty = Type { |
827 | krate, | 828 | krate, |
828 | ty: InEnvironment { | 829 | ty: InEnvironment { |
@@ -830,7 +831,7 @@ impl Function { | |||
830 | environment: environment.clone(), | 831 | environment: environment.clone(), |
831 | }, | 832 | }, |
832 | }; | 833 | }; |
833 | Param { ty } | 834 | Param { func: self, ty, idx } |
834 | }) | 835 | }) |
835 | .collect() | 836 | .collect() |
836 | } | 837 | } |
@@ -893,6 +894,9 @@ impl From<hir_ty::Mutability> for Access { | |||
893 | 894 | ||
894 | #[derive(Debug)] | 895 | #[derive(Debug)] |
895 | pub struct Param { | 896 | pub struct Param { |
897 | func: Function, | ||
898 | /// The index in parameter list, including self parameter. | ||
899 | idx: usize, | ||
896 | ty: Type, | 900 | ty: Type, |
897 | } | 901 | } |
898 | 902 | ||
@@ -900,6 +904,15 @@ impl Param { | |||
900 | pub fn ty(&self) -> &Type { | 904 | pub fn ty(&self) -> &Type { |
901 | &self.ty | 905 | &self.ty |
902 | } | 906 | } |
907 | |||
908 | pub fn pattern_source(&self, db: &dyn HirDatabase) -> Option<ast::Pat> { | ||
909 | let params = self.func.source(db)?.value.param_list()?; | ||
910 | if params.self_param().is_some() { | ||
911 | params.params().nth(self.idx.checked_sub(1)?)?.pat() | ||
912 | } else { | ||
913 | params.params().nth(self.idx)?.pat() | ||
914 | } | ||
915 | } | ||
903 | } | 916 | } |
904 | 917 | ||
905 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 918 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |