aboutsummaryrefslogtreecommitdiff
path: root/crates/ide
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-10-07 12:52:05 +0100
committerGitHub <[email protected]>2020-10-07 12:52:05 +0100
commit83a651b123c08d786d1328293fc0327252a6d5d3 (patch)
tree916f7c1ff5f24d3d27cb14e82044696751414843 /crates/ide
parent5359e8f7429a7b6db7b1ab1ced5b19ff05d79123 (diff)
parent783af171f74d95b498662e5168c3ba320cca8553 (diff)
Merge #6154
6154: Shorten type hints for std::iter Iterators r=SomeoneToIgnore a=Veykril Fixes #3750. This re-exports the `hir_expand::name::known` module to be able to fetch the `Iterator` and `iter` names. I'm not sure if there is anything to do with `Solution::Ambig` in `normalize_trait_assoc_type` or whether discarding those results is always wanted. Co-authored-by: Lukas Wirth <[email protected]>
Diffstat (limited to 'crates/ide')
-rw-r--r--crates/ide/src/inlay_hints.rs201
1 files changed, 162 insertions, 39 deletions
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 3a4dc6a84..7d716577e 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -1,4 +1,5 @@
1use hir::{Adt, Callable, HirDisplay, Semantics, Type}; 1use assists::utils::FamousDefs;
2use hir::{known, HirDisplay, Semantics};
2use ide_db::RootDatabase; 3use ide_db::RootDatabase;
3use stdx::to_lower_snake_case; 4use stdx::to_lower_snake_case;
4use syntax::{ 5use syntax::{
@@ -119,17 +120,18 @@ fn get_chaining_hints(
119 return None; 120 return None;
120 } 121 }
121 if matches!(expr, ast::Expr::PathExpr(_)) { 122 if matches!(expr, ast::Expr::PathExpr(_)) {
122 if let Some(Adt::Struct(st)) = ty.as_adt() { 123 if let Some(hir::Adt::Struct(st)) = ty.as_adt() {
123 if st.fields(sema.db).is_empty() { 124 if st.fields(sema.db).is_empty() {
124 return None; 125 return None;
125 } 126 }
126 } 127 }
127 } 128 }
128 let label = ty.display_truncated(sema.db, config.max_length).to_string();
129 acc.push(InlayHint { 129 acc.push(InlayHint {
130 range: expr.syntax().text_range(), 130 range: expr.syntax().text_range(),
131 kind: InlayKind::ChainingHint, 131 kind: InlayKind::ChainingHint,
132 label: label.into(), 132 label: hint_iterator(sema, config, &ty).unwrap_or_else(|| {
133 ty.display_truncated(sema.db, config.max_length).to_string().into()
134 }),
133 }); 135 });
134 } 136 }
135 Some(()) 137 Some(())
@@ -192,17 +194,58 @@ fn get_bind_pat_hints(
192 if should_not_display_type_hint(sema, &pat, &ty) { 194 if should_not_display_type_hint(sema, &pat, &ty) {
193 return None; 195 return None;
194 } 196 }
195
196 acc.push(InlayHint { 197 acc.push(InlayHint {
197 range: pat.syntax().text_range(), 198 range: pat.syntax().text_range(),
198 kind: InlayKind::TypeHint, 199 kind: InlayKind::TypeHint,
199 label: ty.display_truncated(sema.db, config.max_length).to_string().into(), 200 label: hint_iterator(sema, config, &ty)
201 .unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string().into()),
200 }); 202 });
203
201 Some(()) 204 Some(())
202} 205}
203 206
204fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &Type) -> bool { 207/// Checks if the type is an Iterator from std::iter and replaces its hint with an `impl Iterator<Item = Ty>`.
205 if let Some(Adt::Enum(enum_data)) = pat_ty.as_adt() { 208fn hint_iterator(
209 sema: &Semantics<RootDatabase>,
210 config: &InlayHintsConfig,
211 ty: &hir::Type,
212) -> Option<SmolStr> {
213 let db = sema.db;
214 let strukt = std::iter::successors(Some(ty.clone()), |ty| ty.remove_ref())
215 .last()
216 .and_then(|strukt| strukt.as_adt())?;
217 let krate = strukt.krate(db)?;
218 if krate.declaration_name(db).as_deref() != Some("core") {
219 return None;
220 }
221 let iter_trait = FamousDefs(sema, krate).core_iter_Iterator()?;
222 let iter_mod = FamousDefs(sema, krate).core_iter()?;
223 // assert this type comes from `core::iter`
224 iter_mod.visibility_of(db, &iter_trait.into()).filter(|&vis| vis == hir::Visibility::Public)?;
225 if ty.impls_trait(db, iter_trait, &[]) {
226 let assoc_type_item = iter_trait.items(db).into_iter().find_map(|item| match item {
227 hir::AssocItem::TypeAlias(alias) if alias.name(db) == known::Item => Some(alias),
228 _ => None,
229 })?;
230 if let Some(ty) = ty.normalize_trait_assoc_type(db, iter_trait, &[], assoc_type_item) {
231 const LABEL_START: &str = "impl Iterator<Item = ";
232 const LABEL_END: &str = ">";
233
234 let ty_display = ty.display_truncated(
235 db,
236 config
237 .max_length
238 .map(|len| len.saturating_sub(LABEL_START.len() + LABEL_END.len())),
239 );
240 return Some(format!("{}{}{}", LABEL_START, ty_display, LABEL_END).into());
241 }
242 }
243
244 None
245}
246
247fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &hir::Type) -> bool {
248 if let Some(hir::Adt::Enum(enum_data)) = pat_ty.as_adt() {
206 let pat_text = bind_pat.to_string(); 249 let pat_text = bind_pat.to_string();
207 enum_data 250 enum_data
208 .variants(db) 251 .variants(db)
@@ -217,7 +260,7 @@ fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &Typ
217fn should_not_display_type_hint( 260fn should_not_display_type_hint(
218 sema: &Semantics<RootDatabase>, 261 sema: &Semantics<RootDatabase>,
219 bind_pat: &ast::IdentPat, 262 bind_pat: &ast::IdentPat,
220 pat_ty: &Type, 263 pat_ty: &hir::Type,
221) -> bool { 264) -> bool {
222 let db = sema.db; 265 let db = sema.db;
223 266
@@ -225,7 +268,7 @@ fn should_not_display_type_hint(
225 return true; 268 return true;
226 } 269 }
227 270
228 if let Some(Adt::Struct(s)) = pat_ty.as_adt() { 271 if let Some(hir::Adt::Struct(s)) = pat_ty.as_adt() {
229 if s.fields(db).is_empty() && s.name(db).to_string() == bind_pat.to_string() { 272 if s.fields(db).is_empty() && s.name(db).to_string() == bind_pat.to_string() {
230 return true; 273 return true;
231 } 274 }
@@ -269,7 +312,7 @@ fn should_not_display_type_hint(
269 312
270fn should_show_param_name_hint( 313fn should_show_param_name_hint(
271 sema: &Semantics<RootDatabase>, 314 sema: &Semantics<RootDatabase>,
272 callable: &Callable, 315 callable: &hir::Callable,
273 param_name: &str, 316 param_name: &str,
274 argument: &ast::Expr, 317 argument: &ast::Expr,
275) -> bool { 318) -> bool {
@@ -316,7 +359,7 @@ fn is_enum_name_similar_to_param_name(
316 param_name: &str, 359 param_name: &str,
317) -> bool { 360) -> bool {
318 match sema.type_of_expr(argument).and_then(|t| t.as_adt()) { 361 match sema.type_of_expr(argument).and_then(|t| t.as_adt()) {
319 Some(Adt::Enum(e)) => to_lower_snake_case(&e.name(sema.db).to_string()) == param_name, 362 Some(hir::Adt::Enum(e)) => to_lower_snake_case(&e.name(sema.db).to_string()) == param_name,
320 _ => false, 363 _ => false,
321 } 364 }
322} 365}
@@ -337,7 +380,7 @@ fn is_obvious_param(param_name: &str) -> bool {
337 param_name.len() == 1 || is_obvious_param_name 380 param_name.len() == 1 || is_obvious_param_name
338} 381}
339 382
340fn get_callable(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<Callable> { 383fn get_callable(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Callable> {
341 match expr { 384 match expr {
342 ast::Expr::CallExpr(expr) => sema.type_of_expr(&expr.expr()?)?.as_callable(sema.db), 385 ast::Expr::CallExpr(expr) => sema.type_of_expr(&expr.expr()?)?.as_callable(sema.db),
343 ast::Expr::MethodCallExpr(expr) => sema.resolve_method_call_as_callable(expr), 386 ast::Expr::MethodCallExpr(expr) => sema.resolve_method_call_as_callable(expr),
@@ -347,6 +390,7 @@ fn get_callable(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<Call
347 390
348#[cfg(test)] 391#[cfg(test)]
349mod tests { 392mod tests {
393 use assists::utils::FamousDefs;
350 use expect_test::{expect, Expect}; 394 use expect_test::{expect, Expect};
351 use test_utils::extract_annotations; 395 use test_utils::extract_annotations;
352 396
@@ -357,7 +401,9 @@ mod tests {
357 } 401 }
358 402
359 fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) { 403 fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) {
360 let (analysis, file_id) = fixture::file(ra_fixture); 404 let ra_fixture =
405 format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
406 let (analysis, file_id) = fixture::file(&ra_fixture);
361 let expected = extract_annotations(&*analysis.file_text(file_id).unwrap()); 407 let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
362 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap(); 408 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap();
363 let actual = 409 let actual =
@@ -366,7 +412,9 @@ mod tests {
366 } 412 }
367 413
368 fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) { 414 fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {
369 let (analysis, file_id) = fixture::file(ra_fixture); 415 let ra_fixture =
416 format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
417 let (analysis, file_id) = fixture::file(&ra_fixture);
370 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap(); 418 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap();
371 expect.assert_debug_eq(&inlay_hints) 419 expect.assert_debug_eq(&inlay_hints)
372 } 420 }
@@ -798,12 +846,12 @@ fn main() {
798 expect![[r#" 846 expect![[r#"
799 [ 847 [
800 InlayHint { 848 InlayHint {
801 range: 147..172, 849 range: 148..173,
802 kind: ChainingHint, 850 kind: ChainingHint,
803 label: "B", 851 label: "B",
804 }, 852 },
805 InlayHint { 853 InlayHint {
806 range: 147..154, 854 range: 148..155,
807 kind: ChainingHint, 855 kind: ChainingHint,
808 label: "A", 856 label: "A",
809 }, 857 },
@@ -864,12 +912,12 @@ fn main() {
864 expect![[r#" 912 expect![[r#"
865 [ 913 [
866 InlayHint { 914 InlayHint {
867 range: 143..190, 915 range: 144..191,
868 kind: ChainingHint, 916 kind: ChainingHint,
869 label: "C", 917 label: "C",
870 }, 918 },
871 InlayHint { 919 InlayHint {
872 range: 143..179, 920 range: 144..180,
873 kind: ChainingHint, 921 kind: ChainingHint,
874 label: "B", 922 label: "B",
875 }, 923 },
@@ -909,12 +957,12 @@ fn main() {
909 expect![[r#" 957 expect![[r#"
910 [ 958 [
911 InlayHint { 959 InlayHint {
912 range: 246..283, 960 range: 247..284,
913 kind: ChainingHint, 961 kind: ChainingHint,
914 label: "B<X<i32, bool>>", 962 label: "B<X<i32, bool>>",
915 }, 963 },
916 InlayHint { 964 InlayHint {
917 range: 246..265, 965 range: 247..266,
918 kind: ChainingHint, 966 kind: ChainingHint,
919 label: "A<X<i32, bool>>", 967 label: "A<X<i32, bool>>",
920 }, 968 },
@@ -935,7 +983,6 @@ fn main() {
935 ); 983 );
936 check( 984 check(
937 r#" 985 r#"
938//- /main.rs crate:main deps:core
939pub struct Vec<T> {} 986pub struct Vec<T> {}
940 987
941impl<T> Vec<T> { 988impl<T> Vec<T> {
@@ -956,13 +1003,6 @@ fn main() {
956 println!("Unit expr"); 1003 println!("Unit expr");
957} 1004}
958 1005
959//- /core.rs crate:core
960#[prelude_import] use iter::*;
961mod iter {
962 trait IntoIterator {
963 type Item;
964 }
965}
966//- /alloc.rs crate:alloc deps:core 1006//- /alloc.rs crate:alloc deps:core
967mod collections { 1007mod collections {
968 struct Vec<T> {} 1008 struct Vec<T> {}
@@ -982,7 +1022,6 @@ mod collections {
982 fn complete_for_hint() { 1022 fn complete_for_hint() {
983 check( 1023 check(
984 r#" 1024 r#"
985//- /main.rs crate:main deps:core
986pub struct Vec<T> {} 1025pub struct Vec<T> {}
987 1026
988impl<T> Vec<T> { 1027impl<T> Vec<T> {
@@ -1004,14 +1043,6 @@ fn main() {
1004 //^ &str 1043 //^ &str
1005 } 1044 }
1006} 1045}
1007
1008//- /core.rs crate:core
1009#[prelude_import] use iter::*;
1010mod iter {
1011 trait IntoIterator {
1012 type Item;
1013 }
1014}
1015//- /alloc.rs crate:alloc deps:core 1046//- /alloc.rs crate:alloc deps:core
1016mod collections { 1047mod collections {
1017 struct Vec<T> {} 1048 struct Vec<T> {}
@@ -1037,7 +1068,6 @@ mod collections {
1037 max_length: None, 1068 max_length: None,
1038 }, 1069 },
1039 r#" 1070 r#"
1040//- /main.rs crate:main
1041pub struct Vec<T> {} 1071pub struct Vec<T> {}
1042 1072
1043impl<T> Vec<T> { 1073impl<T> Vec<T> {
@@ -1060,4 +1090,97 @@ fn main() {
1060"#, 1090"#,
1061 ); 1091 );
1062 } 1092 }
1093
1094 #[test]
1095 fn shorten_iterator_hints() {
1096 check_with_config(
1097 InlayHintsConfig {
1098 parameter_hints: false,
1099 type_hints: true,
1100 chaining_hints: false,
1101 max_length: None,
1102 },
1103 r#"
1104use core::iter;
1105
1106struct MyIter;
1107
1108impl Iterator for MyIter {
1109 type Item = ();
1110 fn next(&mut self) -> Option<Self::Item> {
1111 None
1112 }
1113}
1114
1115fn main() {
1116 let _x = MyIter;
1117 //^^ MyIter
1118 let _x = iter::repeat(0);
1119 //^^ impl Iterator<Item = i32>
1120 fn generic<T: Clone>(t: T) {
1121 let _x = iter::repeat(t);
1122 //^^ impl Iterator<Item = T>
1123 let _chained = iter::repeat(t).take(10);
1124 //^^^^^^^^ impl Iterator<Item = T>
1125 }
1126}
1127"#,
1128 );
1129 }
1130
1131 #[test]
1132 fn shorten_iterator_chaining_hints() {
1133 check_expect(
1134 InlayHintsConfig {
1135 parameter_hints: false,
1136 type_hints: false,
1137 chaining_hints: true,
1138 max_length: None,
1139 },
1140 r#"
1141use core::iter;
1142
1143struct MyIter;
1144
1145impl Iterator for MyIter {
1146 type Item = ();
1147 fn next(&mut self) -> Option<Self::Item> {
1148 None
1149 }
1150}
1151
1152fn main() {
1153 let _x = MyIter.by_ref()
1154 .take(5)
1155 .by_ref()
1156 .take(5)
1157 .by_ref();
1158}
1159"#,
1160 expect![[r#"
1161 [
1162 InlayHint {
1163 range: 175..242,
1164 kind: ChainingHint,
1165 label: "impl Iterator<Item = ()>",
1166 },
1167 InlayHint {
1168 range: 175..225,
1169 kind: ChainingHint,
1170 label: "impl Iterator<Item = ()>",
1171 },
1172 InlayHint {
1173 range: 175..207,
1174 kind: ChainingHint,
1175 label: "impl Iterator<Item = ()>",
1176 },
1177 InlayHint {
1178 range: 175..190,
1179 kind: ChainingHint,
1180 label: "&mut MyIter",
1181 },
1182 ]
1183 "#]],
1184 );
1185 }
1063} 1186}