diff options
Diffstat (limited to 'crates/ra_hir_ty/src/display.rs')
-rw-r--r-- | crates/ra_hir_ty/src/display.rs | 373 |
1 files changed, 372 insertions, 1 deletions
diff --git a/crates/ra_hir_ty/src/display.rs b/crates/ra_hir_ty/src/display.rs index d1ff85f0f..14e089cf4 100644 --- a/crates/ra_hir_ty/src/display.rs +++ b/crates/ra_hir_ty/src/display.rs | |||
@@ -2,7 +2,12 @@ | |||
2 | 2 | ||
3 | use std::fmt; | 3 | use std::fmt; |
4 | 4 | ||
5 | use crate::db::HirDatabase; | 5 | use crate::{ |
6 | db::HirDatabase, utils::generics, ApplicationTy, CallableDef, FnSig, GenericPredicate, | ||
7 | Obligation, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, | ||
8 | }; | ||
9 | use hir_def::{generics::TypeParamProvenance, AdtId, AssocContainerId, Lookup}; | ||
10 | use hir_expand::name::Name; | ||
6 | 11 | ||
7 | pub struct HirFormatter<'a, 'b, DB> { | 12 | pub struct HirFormatter<'a, 'b, DB> { |
8 | pub db: &'a DB, | 13 | pub db: &'a DB, |
@@ -97,3 +102,369 @@ where | |||
97 | }) | 102 | }) |
98 | } | 103 | } |
99 | } | 104 | } |
105 | |||
106 | const TYPE_HINT_TRUNCATION: &str = "…"; | ||
107 | |||
108 | impl HirDisplay for &Ty { | ||
109 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { | ||
110 | HirDisplay::hir_fmt(*self, f) | ||
111 | } | ||
112 | } | ||
113 | |||
114 | impl HirDisplay for ApplicationTy { | ||
115 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { | ||
116 | if f.should_truncate() { | ||
117 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | ||
118 | } | ||
119 | |||
120 | match self.ctor { | ||
121 | TypeCtor::Bool => write!(f, "bool")?, | ||
122 | TypeCtor::Char => write!(f, "char")?, | ||
123 | TypeCtor::Int(t) => write!(f, "{}", t)?, | ||
124 | TypeCtor::Float(t) => write!(f, "{}", t)?, | ||
125 | TypeCtor::Str => write!(f, "str")?, | ||
126 | TypeCtor::Slice => { | ||
127 | let t = self.parameters.as_single(); | ||
128 | write!(f, "[{}]", t.display(f.db))?; | ||
129 | } | ||
130 | TypeCtor::Array => { | ||
131 | let t = self.parameters.as_single(); | ||
132 | write!(f, "[{}; _]", t.display(f.db))?; | ||
133 | } | ||
134 | TypeCtor::RawPtr(m) => { | ||
135 | let t = self.parameters.as_single(); | ||
136 | write!(f, "*{}{}", m.as_keyword_for_ptr(), t.display(f.db))?; | ||
137 | } | ||
138 | TypeCtor::Ref(m) => { | ||
139 | let t = self.parameters.as_single(); | ||
140 | let ty_display = if f.omit_verbose_types() { | ||
141 | t.display_truncated(f.db, f.max_size) | ||
142 | } else { | ||
143 | t.display(f.db) | ||
144 | }; | ||
145 | write!(f, "&{}{}", m.as_keyword_for_ref(), ty_display)?; | ||
146 | } | ||
147 | TypeCtor::Never => write!(f, "!")?, | ||
148 | TypeCtor::Tuple { .. } => { | ||
149 | let ts = &self.parameters; | ||
150 | if ts.len() == 1 { | ||
151 | write!(f, "({},)", ts[0].display(f.db))?; | ||
152 | } else { | ||
153 | write!(f, "(")?; | ||
154 | f.write_joined(&*ts.0, ", ")?; | ||
155 | write!(f, ")")?; | ||
156 | } | ||
157 | } | ||
158 | TypeCtor::FnPtr { .. } => { | ||
159 | let sig = FnSig::from_fn_ptr_substs(&self.parameters); | ||
160 | write!(f, "fn(")?; | ||
161 | f.write_joined(sig.params(), ", ")?; | ||
162 | write!(f, ") -> {}", sig.ret().display(f.db))?; | ||
163 | } | ||
164 | TypeCtor::FnDef(def) => { | ||
165 | let sig = f.db.callable_item_signature(def).subst(&self.parameters); | ||
166 | let name = match def { | ||
167 | CallableDef::FunctionId(ff) => f.db.function_data(ff).name.clone(), | ||
168 | CallableDef::StructId(s) => f.db.struct_data(s).name.clone(), | ||
169 | CallableDef::EnumVariantId(e) => { | ||
170 | let enum_data = f.db.enum_data(e.parent); | ||
171 | enum_data.variants[e.local_id].name.clone() | ||
172 | } | ||
173 | }; | ||
174 | match def { | ||
175 | CallableDef::FunctionId(_) => write!(f, "fn {}", name)?, | ||
176 | CallableDef::StructId(_) | CallableDef::EnumVariantId(_) => { | ||
177 | write!(f, "{}", name)? | ||
178 | } | ||
179 | } | ||
180 | if self.parameters.len() > 0 { | ||
181 | let generics = generics(f.db, def.into()); | ||
182 | let (parent_params, self_param, type_params, _impl_trait_params) = | ||
183 | generics.provenance_split(); | ||
184 | let total_len = parent_params + self_param + type_params; | ||
185 | // We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self? | ||
186 | if total_len > 0 { | ||
187 | write!(f, "<")?; | ||
188 | f.write_joined(&self.parameters.0[..total_len], ", ")?; | ||
189 | write!(f, ">")?; | ||
190 | } | ||
191 | } | ||
192 | write!(f, "(")?; | ||
193 | f.write_joined(sig.params(), ", ")?; | ||
194 | write!(f, ") -> {}", sig.ret().display(f.db))?; | ||
195 | } | ||
196 | TypeCtor::Adt(def_id) => { | ||
197 | let name = match def_id { | ||
198 | AdtId::StructId(it) => f.db.struct_data(it).name.clone(), | ||
199 | AdtId::UnionId(it) => f.db.union_data(it).name.clone(), | ||
200 | AdtId::EnumId(it) => f.db.enum_data(it).name.clone(), | ||
201 | }; | ||
202 | write!(f, "{}", name)?; | ||
203 | if self.parameters.len() > 0 { | ||
204 | write!(f, "<")?; | ||
205 | |||
206 | let mut non_default_parameters = Vec::with_capacity(self.parameters.len()); | ||
207 | let parameters_to_write = if f.omit_verbose_types() { | ||
208 | match self | ||
209 | .ctor | ||
210 | .as_generic_def() | ||
211 | .map(|generic_def_id| f.db.generic_defaults(generic_def_id)) | ||
212 | .filter(|defaults| !defaults.is_empty()) | ||
213 | { | ||
214 | Option::None => self.parameters.0.as_ref(), | ||
215 | Option::Some(default_parameters) => { | ||
216 | for (i, parameter) in self.parameters.iter().enumerate() { | ||
217 | match (parameter, default_parameters.get(i)) { | ||
218 | (&Ty::Unknown, _) | (_, None) => { | ||
219 | non_default_parameters.push(parameter.clone()) | ||
220 | } | ||
221 | (_, Some(default_parameter)) | ||
222 | if parameter != default_parameter => | ||
223 | { | ||
224 | non_default_parameters.push(parameter.clone()) | ||
225 | } | ||
226 | _ => (), | ||
227 | } | ||
228 | } | ||
229 | &non_default_parameters | ||
230 | } | ||
231 | } | ||
232 | } else { | ||
233 | self.parameters.0.as_ref() | ||
234 | }; | ||
235 | |||
236 | f.write_joined(parameters_to_write, ", ")?; | ||
237 | write!(f, ">")?; | ||
238 | } | ||
239 | } | ||
240 | TypeCtor::AssociatedType(type_alias) => { | ||
241 | let trait_ = match type_alias.lookup(f.db).container { | ||
242 | AssocContainerId::TraitId(it) => it, | ||
243 | _ => panic!("not an associated type"), | ||
244 | }; | ||
245 | let trait_name = f.db.trait_data(trait_).name.clone(); | ||
246 | let name = f.db.type_alias_data(type_alias).name.clone(); | ||
247 | write!(f, "{}::{}", trait_name, name)?; | ||
248 | if self.parameters.len() > 0 { | ||
249 | write!(f, "<")?; | ||
250 | f.write_joined(&*self.parameters.0, ", ")?; | ||
251 | write!(f, ">")?; | ||
252 | } | ||
253 | } | ||
254 | TypeCtor::Closure { .. } => { | ||
255 | let sig = self.parameters[0] | ||
256 | .callable_sig(f.db) | ||
257 | .expect("first closure parameter should contain signature"); | ||
258 | let return_type_hint = sig.ret().display(f.db); | ||
259 | if sig.params().is_empty() { | ||
260 | write!(f, "|| -> {}", return_type_hint)?; | ||
261 | } else if f.omit_verbose_types() { | ||
262 | write!(f, "|{}| -> {}", TYPE_HINT_TRUNCATION, return_type_hint)?; | ||
263 | } else { | ||
264 | write!(f, "|")?; | ||
265 | f.write_joined(sig.params(), ", ")?; | ||
266 | write!(f, "| -> {}", return_type_hint)?; | ||
267 | }; | ||
268 | } | ||
269 | } | ||
270 | Ok(()) | ||
271 | } | ||
272 | } | ||
273 | |||
274 | impl HirDisplay for ProjectionTy { | ||
275 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { | ||
276 | if f.should_truncate() { | ||
277 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | ||
278 | } | ||
279 | |||
280 | let trait_name = f.db.trait_data(self.trait_(f.db)).name.clone(); | ||
281 | write!(f, "<{} as {}", self.parameters[0].display(f.db), trait_name,)?; | ||
282 | if self.parameters.len() > 1 { | ||
283 | write!(f, "<")?; | ||
284 | f.write_joined(&self.parameters[1..], ", ")?; | ||
285 | write!(f, ">")?; | ||
286 | } | ||
287 | write!(f, ">::{}", f.db.type_alias_data(self.associated_ty).name)?; | ||
288 | Ok(()) | ||
289 | } | ||
290 | } | ||
291 | |||
292 | impl HirDisplay for Ty { | ||
293 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { | ||
294 | if f.should_truncate() { | ||
295 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | ||
296 | } | ||
297 | |||
298 | match self { | ||
299 | Ty::Apply(a_ty) => a_ty.hir_fmt(f)?, | ||
300 | Ty::Projection(p_ty) => p_ty.hir_fmt(f)?, | ||
301 | Ty::Placeholder(id) => { | ||
302 | let generics = generics(f.db, id.parent); | ||
303 | let param_data = &generics.params.types[id.local_id]; | ||
304 | match param_data.provenance { | ||
305 | TypeParamProvenance::TypeParamList | TypeParamProvenance::TraitSelf => { | ||
306 | write!(f, "{}", param_data.name.clone().unwrap_or_else(Name::missing))? | ||
307 | } | ||
308 | TypeParamProvenance::ArgumentImplTrait => { | ||
309 | write!(f, "impl ")?; | ||
310 | let bounds = f.db.generic_predicates_for_param(*id); | ||
311 | let substs = Substs::type_params_for_generics(&generics); | ||
312 | write_bounds_like_dyn_trait( | ||
313 | &bounds.iter().map(|b| b.clone().subst(&substs)).collect::<Vec<_>>(), | ||
314 | f, | ||
315 | )?; | ||
316 | } | ||
317 | } | ||
318 | } | ||
319 | Ty::Bound(idx) => write!(f, "?{}", idx)?, | ||
320 | Ty::Dyn(predicates) | Ty::Opaque(predicates) => { | ||
321 | match self { | ||
322 | Ty::Dyn(_) => write!(f, "dyn ")?, | ||
323 | Ty::Opaque(_) => write!(f, "impl ")?, | ||
324 | _ => unreachable!(), | ||
325 | }; | ||
326 | write_bounds_like_dyn_trait(&predicates, f)?; | ||
327 | } | ||
328 | Ty::Unknown => write!(f, "{{unknown}}")?, | ||
329 | Ty::Infer(..) => write!(f, "_")?, | ||
330 | } | ||
331 | Ok(()) | ||
332 | } | ||
333 | } | ||
334 | |||
335 | fn write_bounds_like_dyn_trait( | ||
336 | predicates: &[GenericPredicate], | ||
337 | f: &mut HirFormatter<impl HirDatabase>, | ||
338 | ) -> fmt::Result { | ||
339 | // Note: This code is written to produce nice results (i.e. | ||
340 | // corresponding to surface Rust) for types that can occur in | ||
341 | // actual Rust. It will have weird results if the predicates | ||
342 | // aren't as expected (i.e. self types = $0, projection | ||
343 | // predicates for a certain trait come after the Implemented | ||
344 | // predicate for that trait). | ||
345 | let mut first = true; | ||
346 | let mut angle_open = false; | ||
347 | for p in predicates.iter() { | ||
348 | match p { | ||
349 | GenericPredicate::Implemented(trait_ref) => { | ||
350 | if angle_open { | ||
351 | write!(f, ">")?; | ||
352 | } | ||
353 | if !first { | ||
354 | write!(f, " + ")?; | ||
355 | } | ||
356 | // We assume that the self type is $0 (i.e. the | ||
357 | // existential) here, which is the only thing that's | ||
358 | // possible in actual Rust, and hence don't print it | ||
359 | write!(f, "{}", f.db.trait_data(trait_ref.trait_).name.clone())?; | ||
360 | if trait_ref.substs.len() > 1 { | ||
361 | write!(f, "<")?; | ||
362 | f.write_joined(&trait_ref.substs[1..], ", ")?; | ||
363 | // there might be assoc type bindings, so we leave the angle brackets open | ||
364 | angle_open = true; | ||
365 | } | ||
366 | } | ||
367 | GenericPredicate::Projection(projection_pred) => { | ||
368 | // in types in actual Rust, these will always come | ||
369 | // after the corresponding Implemented predicate | ||
370 | if angle_open { | ||
371 | write!(f, ", ")?; | ||
372 | } else { | ||
373 | write!(f, "<")?; | ||
374 | angle_open = true; | ||
375 | } | ||
376 | let name = | ||
377 | f.db.type_alias_data(projection_pred.projection_ty.associated_ty).name.clone(); | ||
378 | write!(f, "{} = ", name)?; | ||
379 | projection_pred.ty.hir_fmt(f)?; | ||
380 | } | ||
381 | GenericPredicate::Error => { | ||
382 | if angle_open { | ||
383 | // impl Trait<X, {error}> | ||
384 | write!(f, ", ")?; | ||
385 | } else if !first { | ||
386 | // impl Trait + {error} | ||
387 | write!(f, " + ")?; | ||
388 | } | ||
389 | p.hir_fmt(f)?; | ||
390 | } | ||
391 | } | ||
392 | first = false; | ||
393 | } | ||
394 | if angle_open { | ||
395 | write!(f, ">")?; | ||
396 | } | ||
397 | Ok(()) | ||
398 | } | ||
399 | |||
400 | impl TraitRef { | ||
401 | fn hir_fmt_ext(&self, f: &mut HirFormatter<impl HirDatabase>, use_as: bool) -> fmt::Result { | ||
402 | if f.should_truncate() { | ||
403 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | ||
404 | } | ||
405 | |||
406 | self.substs[0].hir_fmt(f)?; | ||
407 | if use_as { | ||
408 | write!(f, " as ")?; | ||
409 | } else { | ||
410 | write!(f, ": ")?; | ||
411 | } | ||
412 | write!(f, "{}", f.db.trait_data(self.trait_).name.clone())?; | ||
413 | if self.substs.len() > 1 { | ||
414 | write!(f, "<")?; | ||
415 | f.write_joined(&self.substs[1..], ", ")?; | ||
416 | write!(f, ">")?; | ||
417 | } | ||
418 | Ok(()) | ||
419 | } | ||
420 | } | ||
421 | |||
422 | impl HirDisplay for TraitRef { | ||
423 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { | ||
424 | self.hir_fmt_ext(f, false) | ||
425 | } | ||
426 | } | ||
427 | |||
428 | impl HirDisplay for &GenericPredicate { | ||
429 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { | ||
430 | HirDisplay::hir_fmt(*self, f) | ||
431 | } | ||
432 | } | ||
433 | |||
434 | impl HirDisplay for GenericPredicate { | ||
435 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { | ||
436 | if f.should_truncate() { | ||
437 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | ||
438 | } | ||
439 | |||
440 | match self { | ||
441 | GenericPredicate::Implemented(trait_ref) => trait_ref.hir_fmt(f)?, | ||
442 | GenericPredicate::Projection(projection_pred) => { | ||
443 | write!(f, "<")?; | ||
444 | projection_pred.projection_ty.trait_ref(f.db).hir_fmt_ext(f, true)?; | ||
445 | write!( | ||
446 | f, | ||
447 | ">::{} = {}", | ||
448 | f.db.type_alias_data(projection_pred.projection_ty.associated_ty).name, | ||
449 | projection_pred.ty.display(f.db) | ||
450 | )?; | ||
451 | } | ||
452 | GenericPredicate::Error => write!(f, "{{error}}")?, | ||
453 | } | ||
454 | Ok(()) | ||
455 | } | ||
456 | } | ||
457 | |||
458 | impl HirDisplay for Obligation { | ||
459 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { | ||
460 | match self { | ||
461 | Obligation::Trait(tr) => write!(f, "Implements({})", tr.display(f.db)), | ||
462 | Obligation::Projection(proj) => write!( | ||
463 | f, | ||
464 | "Normalize({} => {})", | ||
465 | proj.projection_ty.display(f.db), | ||
466 | proj.ty.display(f.db) | ||
467 | ), | ||
468 | } | ||
469 | } | ||
470 | } | ||