aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_ty
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-05-09 10:29:11 +0100
committerGitHub <[email protected]>2020-05-09 10:29:11 +0100
commit25e37e2c93ba91a8956fdff8bbe9701c623d386f (patch)
treec8c3d772e9ad5a71f7ea911b4c82a2f373211143 /crates/ra_hir_ty
parentd7a0b0ff913d717fa6e7c5a1db02a30316760a75 (diff)
parent64e6b8200bfceea92b0dcaab3e533a9152994e78 (diff)
Merge #4175
4175: Introduce HirDisplay method for rendering source code & use it in add_function assist r=flodiebold a=TimoFreiberg Next feature for #3639. So far the only change in the new `HirDisplay` method is that paths are qualified, but more changes will be necessary (omitting the function name from function types, returning an error instead of printing `"{unknown}"`, probably more). Is that approach okay? Co-authored-by: Timo Freiberg <[email protected]>
Diffstat (limited to 'crates/ra_hir_ty')
-rw-r--r--crates/ra_hir_ty/src/display.rs176
-rw-r--r--crates/ra_hir_ty/src/tests.rs17
-rw-r--r--crates/ra_hir_ty/src/tests/display_source_code.rs23
3 files changed, 178 insertions, 38 deletions
diff --git a/crates/ra_hir_ty/src/display.rs b/crates/ra_hir_ty/src/display.rs
index d03bbd5a7..f5edaea8c 100644
--- a/crates/ra_hir_ty/src/display.rs
+++ b/crates/ra_hir_ty/src/display.rs
@@ -6,28 +6,42 @@ 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, ProjectionTy, Substs, TraitRef, Ty, TypeCtor,
8}; 8};
9use hir_def::{generics::TypeParamProvenance, AdtId, AssocContainerId, Lookup}; 9use hir_def::{
10 find_path, generics::TypeParamProvenance, item_scope::ItemInNs, AdtId, AssocContainerId,
11 Lookup, ModuleId,
12};
10use hir_expand::name::Name; 13use hir_expand::name::Name;
11 14
12pub struct HirFormatter<'a, 'b> { 15pub struct HirFormatter<'a> {
13 pub db: &'a dyn HirDatabase, 16 pub db: &'a dyn HirDatabase,
14 fmt: &'a mut fmt::Formatter<'b>, 17 fmt: &'a mut dyn fmt::Write,
15 buf: String, 18 buf: String,
16 curr_size: usize, 19 curr_size: usize,
17 pub(crate) max_size: Option<usize>, 20 pub(crate) max_size: Option<usize>,
18 omit_verbose_types: bool, 21 omit_verbose_types: bool,
22 display_target: DisplayTarget,
19} 23}
20 24
21pub trait HirDisplay { 25pub trait HirDisplay {
22 fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result; 26 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError>;
23 27
28 /// Returns a `Display`able type that is human-readable.
29 /// Use this for showing types to the user (e.g. diagnostics)
24 fn display<'a>(&'a self, db: &'a dyn HirDatabase) -> HirDisplayWrapper<'a, Self> 30 fn display<'a>(&'a self, db: &'a dyn HirDatabase) -> HirDisplayWrapper<'a, Self>
25 where 31 where
26 Self: Sized, 32 Self: Sized,
27 { 33 {
28 HirDisplayWrapper(db, self, None, false) 34 HirDisplayWrapper {
35 db,
36 t: self,
37 max_size: None,
38 omit_verbose_types: false,
39 display_target: DisplayTarget::Diagnostics,
40 }
29 } 41 }
30 42
43 /// Returns a `Display`able type that is human-readable and tries to be succinct.
44 /// Use this for showing types to the user where space is constrained (e.g. doc popups)
31 fn display_truncated<'a>( 45 fn display_truncated<'a>(
32 &'a self, 46 &'a self,
33 db: &'a dyn HirDatabase, 47 db: &'a dyn HirDatabase,
@@ -36,16 +50,46 @@ pub trait HirDisplay {
36 where 50 where
37 Self: Sized, 51 Self: Sized,
38 { 52 {
39 HirDisplayWrapper(db, self, max_size, true) 53 HirDisplayWrapper {
54 db,
55 t: self,
56 max_size,
57 omit_verbose_types: true,
58 display_target: DisplayTarget::Diagnostics,
59 }
60 }
61
62 /// Returns a String representation of `self` that can be inserted into the given module.
63 /// Use this when generating code (e.g. assists)
64 fn display_source_code<'a>(
65 &'a self,
66 db: &'a dyn HirDatabase,
67 module_id: ModuleId,
68 ) -> Result<String, DisplaySourceCodeError> {
69 let mut result = String::new();
70 match self.hir_fmt(&mut HirFormatter {
71 db,
72 fmt: &mut result,
73 buf: String::with_capacity(20),
74 curr_size: 0,
75 max_size: None,
76 omit_verbose_types: false,
77 display_target: DisplayTarget::SourceCode { module_id },
78 }) {
79 Ok(()) => {}
80 Err(HirDisplayError::FmtError) => panic!("Writing to String can't fail!"),
81 Err(HirDisplayError::DisplaySourceCodeError(e)) => return Err(e),
82 };
83 Ok(result)
40 } 84 }
41} 85}
42 86
43impl<'a, 'b> HirFormatter<'a, 'b> { 87impl<'a> HirFormatter<'a> {
44 pub fn write_joined<T: HirDisplay>( 88 pub fn write_joined<T: HirDisplay>(
45 &mut self, 89 &mut self,
46 iter: impl IntoIterator<Item = T>, 90 iter: impl IntoIterator<Item = T>,
47 sep: &str, 91 sep: &str,
48 ) -> fmt::Result { 92 ) -> Result<(), HirDisplayError> {
49 let mut first = true; 93 let mut first = true;
50 for e in iter { 94 for e in iter {
51 if !first { 95 if !first {
@@ -58,14 +102,14 @@ impl<'a, 'b> HirFormatter<'a, 'b> {
58 } 102 }
59 103
60 /// This allows using the `write!` macro directly with a `HirFormatter`. 104 /// This allows using the `write!` macro directly with a `HirFormatter`.
61 pub fn write_fmt(&mut self, args: fmt::Arguments) -> fmt::Result { 105 pub fn write_fmt(&mut self, args: fmt::Arguments) -> Result<(), HirDisplayError> {
62 // We write to a buffer first to track output size 106 // We write to a buffer first to track output size
63 self.buf.clear(); 107 self.buf.clear();
64 fmt::write(&mut self.buf, args)?; 108 fmt::write(&mut self.buf, args)?;
65 self.curr_size += self.buf.len(); 109 self.curr_size += self.buf.len();
66 110
67 // Then we write to the internal formatter from the buffer 111 // Then we write to the internal formatter from the buffer
68 self.fmt.write_str(&self.buf) 112 self.fmt.write_str(&self.buf).map_err(HirDisplayError::from)
69 } 113 }
70 114
71 pub fn should_truncate(&self) -> bool { 115 pub fn should_truncate(&self) -> bool {
@@ -81,34 +125,76 @@ impl<'a, 'b> HirFormatter<'a, 'b> {
81 } 125 }
82} 126}
83 127
84pub struct HirDisplayWrapper<'a, T>(&'a dyn HirDatabase, &'a T, Option<usize>, bool); 128#[derive(Clone, Copy)]
129enum DisplayTarget {
130 /// Display types for inlays, doc popups, autocompletion, etc...
131 /// Showing `{unknown}` or not qualifying paths is fine here.
132 /// There's no reason for this to fail.
133 Diagnostics,
134 /// Display types for inserting them in source files.
135 /// The generated code should compile, so paths need to be qualified.
136 SourceCode { module_id: ModuleId },
137}
138
139#[derive(Debug)]
140pub enum DisplaySourceCodeError {
141 PathNotFound,
142}
143
144pub enum HirDisplayError {
145 /// Errors that can occur when generating source code
146 DisplaySourceCodeError(DisplaySourceCodeError),
147 /// `FmtError` is required to be compatible with std::fmt::Display
148 FmtError,
149}
150impl From<fmt::Error> for HirDisplayError {
151 fn from(_: fmt::Error) -> Self {
152 Self::FmtError
153 }
154}
155
156pub struct HirDisplayWrapper<'a, T> {
157 db: &'a dyn HirDatabase,
158 t: &'a T,
159 max_size: Option<usize>,
160 omit_verbose_types: bool,
161 display_target: DisplayTarget,
162}
85 163
86impl<'a, T> fmt::Display for HirDisplayWrapper<'a, T> 164impl<'a, T> fmt::Display for HirDisplayWrapper<'a, T>
87where 165where
88 T: HirDisplay, 166 T: HirDisplay,
89{ 167{
90 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 168 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
91 self.1.hir_fmt(&mut HirFormatter { 169 match self.t.hir_fmt(&mut HirFormatter {
92 db: self.0, 170 db: self.db,
93 fmt: f, 171 fmt: f,
94 buf: String::with_capacity(20), 172 buf: String::with_capacity(20),
95 curr_size: 0, 173 curr_size: 0,
96 max_size: self.2, 174 max_size: self.max_size,
97 omit_verbose_types: self.3, 175 omit_verbose_types: self.omit_verbose_types,
98 }) 176 display_target: self.display_target,
177 }) {
178 Ok(()) => Ok(()),
179 Err(HirDisplayError::FmtError) => Err(fmt::Error),
180 Err(HirDisplayError::DisplaySourceCodeError(_)) => {
181 // This should never happen
182 panic!("HirDisplay failed when calling Display::fmt!")
183 }
184 }
99 } 185 }
100} 186}
101 187
102const TYPE_HINT_TRUNCATION: &str = "…"; 188const TYPE_HINT_TRUNCATION: &str = "…";
103 189
104impl HirDisplay for &Ty { 190impl HirDisplay for &Ty {
105 fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { 191 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
106 HirDisplay::hir_fmt(*self, f) 192 HirDisplay::hir_fmt(*self, f)
107 } 193 }
108} 194}
109 195
110impl HirDisplay for ApplicationTy { 196impl HirDisplay for ApplicationTy {
111 fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { 197 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
112 if f.should_truncate() { 198 if f.should_truncate() {
113 return write!(f, "{}", TYPE_HINT_TRUNCATION); 199 return write!(f, "{}", TYPE_HINT_TRUNCATION);
114 } 200 }
@@ -191,12 +277,30 @@ impl HirDisplay for ApplicationTy {
191 } 277 }
192 } 278 }
193 TypeCtor::Adt(def_id) => { 279 TypeCtor::Adt(def_id) => {
194 let name = match def_id { 280 match f.display_target {
195 AdtId::StructId(it) => f.db.struct_data(it).name.clone(), 281 DisplayTarget::Diagnostics => {
196 AdtId::UnionId(it) => f.db.union_data(it).name.clone(), 282 let name = match def_id {
197 AdtId::EnumId(it) => f.db.enum_data(it).name.clone(), 283 AdtId::StructId(it) => f.db.struct_data(it).name.clone(),
198 }; 284 AdtId::UnionId(it) => f.db.union_data(it).name.clone(),
199 write!(f, "{}", name)?; 285 AdtId::EnumId(it) => f.db.enum_data(it).name.clone(),
286 };
287 write!(f, "{}", name)?;
288 }
289 DisplayTarget::SourceCode { module_id } => {
290 if let Some(path) = find_path::find_path(
291 f.db.upcast(),
292 ItemInNs::Types(def_id.into()),
293 module_id,
294 ) {
295 write!(f, "{}", path)?;
296 } else {
297 return Err(HirDisplayError::DisplaySourceCodeError(
298 DisplaySourceCodeError::PathNotFound,
299 ));
300 }
301 }
302 }
303
200 if self.parameters.len() > 0 { 304 if self.parameters.len() > 0 {
201 let mut non_default_parameters = Vec::with_capacity(self.parameters.len()); 305 let mut non_default_parameters = Vec::with_capacity(self.parameters.len());
202 let parameters_to_write = if f.omit_verbose_types() { 306 let parameters_to_write = if f.omit_verbose_types() {
@@ -269,7 +373,7 @@ impl HirDisplay for ApplicationTy {
269} 373}
270 374
271impl HirDisplay for ProjectionTy { 375impl HirDisplay for ProjectionTy {
272 fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { 376 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
273 if f.should_truncate() { 377 if f.should_truncate() {
274 return write!(f, "{}", TYPE_HINT_TRUNCATION); 378 return write!(f, "{}", TYPE_HINT_TRUNCATION);
275 } 379 }
@@ -287,7 +391,7 @@ impl HirDisplay for ProjectionTy {
287} 391}
288 392
289impl HirDisplay for Ty { 393impl HirDisplay for Ty {
290 fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { 394 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
291 if f.should_truncate() { 395 if f.should_truncate() {
292 return write!(f, "{}", TYPE_HINT_TRUNCATION); 396 return write!(f, "{}", TYPE_HINT_TRUNCATION);
293 } 397 }
@@ -332,7 +436,7 @@ impl HirDisplay for Ty {
332fn write_bounds_like_dyn_trait( 436fn write_bounds_like_dyn_trait(
333 predicates: &[GenericPredicate], 437 predicates: &[GenericPredicate],
334 f: &mut HirFormatter, 438 f: &mut HirFormatter,
335) -> fmt::Result { 439) -> Result<(), HirDisplayError> {
336 // Note: This code is written to produce nice results (i.e. 440 // Note: This code is written to produce nice results (i.e.
337 // corresponding to surface Rust) for types that can occur in 441 // corresponding to surface Rust) for types that can occur in
338 // actual Rust. It will have weird results if the predicates 442 // actual Rust. It will have weird results if the predicates
@@ -394,7 +498,7 @@ fn write_bounds_like_dyn_trait(
394} 498}
395 499
396impl TraitRef { 500impl TraitRef {
397 fn hir_fmt_ext(&self, f: &mut HirFormatter, use_as: bool) -> fmt::Result { 501 fn hir_fmt_ext(&self, f: &mut HirFormatter, use_as: bool) -> Result<(), HirDisplayError> {
398 if f.should_truncate() { 502 if f.should_truncate() {
399 return write!(f, "{}", TYPE_HINT_TRUNCATION); 503 return write!(f, "{}", TYPE_HINT_TRUNCATION);
400 } 504 }
@@ -416,19 +520,19 @@ impl TraitRef {
416} 520}
417 521
418impl HirDisplay for TraitRef { 522impl HirDisplay for TraitRef {
419 fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { 523 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
420 self.hir_fmt_ext(f, false) 524 self.hir_fmt_ext(f, false)
421 } 525 }
422} 526}
423 527
424impl HirDisplay for &GenericPredicate { 528impl HirDisplay for &GenericPredicate {
425 fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { 529 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
426 HirDisplay::hir_fmt(*self, f) 530 HirDisplay::hir_fmt(*self, f)
427 } 531 }
428} 532}
429 533
430impl HirDisplay for GenericPredicate { 534impl HirDisplay for GenericPredicate {
431 fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { 535 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
432 if f.should_truncate() { 536 if f.should_truncate() {
433 return write!(f, "{}", TYPE_HINT_TRUNCATION); 537 return write!(f, "{}", TYPE_HINT_TRUNCATION);
434 } 538 }
@@ -452,15 +556,15 @@ impl HirDisplay for GenericPredicate {
452} 556}
453 557
454impl HirDisplay for Obligation { 558impl HirDisplay for Obligation {
455 fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { 559 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
456 match self { 560 Ok(match self {
457 Obligation::Trait(tr) => write!(f, "Implements({})", tr.display(f.db)), 561 Obligation::Trait(tr) => write!(f, "Implements({})", tr.display(f.db))?,
458 Obligation::Projection(proj) => write!( 562 Obligation::Projection(proj) => write!(
459 f, 563 f,
460 "Normalize({} => {})", 564 "Normalize({} => {})",
461 proj.projection_ty.display(f.db), 565 proj.projection_ty.display(f.db),
462 proj.ty.display(f.db) 566 proj.ty.display(f.db)
463 ), 567 )?,
464 } 568 })
465 } 569 }
466} 570}
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs
index 5af88b368..1fe05c70c 100644
--- a/crates/ra_hir_ty/src/tests.rs
+++ b/crates/ra_hir_ty/src/tests.rs
@@ -6,6 +6,7 @@ mod patterns;
6mod traits; 6mod traits;
7mod method_resolution; 7mod method_resolution;
8mod macros; 8mod macros;
9mod display_source_code;
9 10
10use std::sync::Arc; 11use std::sync::Arc;
11 12
@@ -16,7 +17,7 @@ use hir_def::{
16 item_scope::ItemScope, 17 item_scope::ItemScope,
17 keys, 18 keys,
18 nameres::CrateDefMap, 19 nameres::CrateDefMap,
19 AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, 20 AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, ModuleId,
20}; 21};
21use hir_expand::{db::AstDatabase, InFile}; 22use hir_expand::{db::AstDatabase, InFile};
22use insta::assert_snapshot; 23use insta::assert_snapshot;
@@ -37,6 +38,18 @@ use crate::{
37// update the snapshots. 38// update the snapshots.
38 39
39fn type_at_pos(db: &TestDB, pos: FilePosition) -> String { 40fn type_at_pos(db: &TestDB, pos: FilePosition) -> String {
41 type_at_pos_displayed(db, pos, |ty, _| ty.display(db).to_string())
42}
43
44fn displayed_source_at_pos(db: &TestDB, pos: FilePosition) -> String {
45 type_at_pos_displayed(db, pos, |ty, module_id| ty.display_source_code(db, module_id).unwrap())
46}
47
48fn type_at_pos_displayed(
49 db: &TestDB,
50 pos: FilePosition,
51 display_fn: impl FnOnce(&Ty, ModuleId) -> String,
52) -> String {
40 let file = db.parse(pos.file_id).ok().unwrap(); 53 let file = db.parse(pos.file_id).ok().unwrap();
41 let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap(); 54 let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap();
42 let fn_def = expr.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); 55 let fn_def = expr.syntax().ancestors().find_map(ast::FnDef::cast).unwrap();
@@ -49,7 +62,7 @@ fn type_at_pos(db: &TestDB, pos: FilePosition) -> String {
49 if let Some(expr_id) = source_map.node_expr(InFile::new(pos.file_id.into(), &expr)) { 62 if let Some(expr_id) = source_map.node_expr(InFile::new(pos.file_id.into(), &expr)) {
50 let infer = db.infer(func.into()); 63 let infer = db.infer(func.into());
51 let ty = &infer[expr_id]; 64 let ty = &infer[expr_id];
52 return ty.display(db).to_string(); 65 return display_fn(ty, module);
53 } 66 }
54 panic!("Can't find expression") 67 panic!("Can't find expression")
55} 68}
diff --git a/crates/ra_hir_ty/src/tests/display_source_code.rs b/crates/ra_hir_ty/src/tests/display_source_code.rs
new file mode 100644
index 000000000..ca1748615
--- /dev/null
+++ b/crates/ra_hir_ty/src/tests/display_source_code.rs
@@ -0,0 +1,23 @@
1use super::displayed_source_at_pos;
2use crate::test_db::TestDB;
3use ra_db::fixture::WithFixture;
4
5#[test]
6fn qualify_path_to_submodule() {
7 let (db, pos) = TestDB::with_position(
8 r#"
9//- /main.rs
10
11mod foo {
12 pub struct Foo;
13}
14
15fn bar() {
16 let foo: foo::Foo = foo::Foo;
17 foo<|>
18}
19
20"#,
21 );
22 assert_eq!("foo::Foo", displayed_source_at_pos(&db, pos));
23}