diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-10-07 12:52:05 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2020-10-07 12:52:05 +0100 |
commit | 83a651b123c08d786d1328293fc0327252a6d5d3 (patch) | |
tree | 916f7c1ff5f24d3d27cb14e82044696751414843 /crates/ide | |
parent | 5359e8f7429a7b6db7b1ab1ced5b19ff05d79123 (diff) | |
parent | 783af171f74d95b498662e5168c3ba320cca8553 (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.rs | 201 |
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 @@ | |||
1 | use hir::{Adt, Callable, HirDisplay, Semantics, Type}; | 1 | use assists::utils::FamousDefs; |
2 | use hir::{known, HirDisplay, Semantics}; | ||
2 | use ide_db::RootDatabase; | 3 | use ide_db::RootDatabase; |
3 | use stdx::to_lower_snake_case; | 4 | use stdx::to_lower_snake_case; |
4 | use syntax::{ | 5 | use 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 | ||
204 | fn 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() { | 208 | fn 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 | |||
247 | fn 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 | |||
217 | fn should_not_display_type_hint( | 260 | fn 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 | ||
270 | fn should_show_param_name_hint( | 313 | fn 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 | ||
340 | fn get_callable(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<Callable> { | 383 | fn 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)] |
349 | mod tests { | 392 | mod 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 | ||
939 | pub struct Vec<T> {} | 986 | pub struct Vec<T> {} |
940 | 987 | ||
941 | impl<T> Vec<T> { | 988 | impl<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::*; | ||
961 | mod iter { | ||
962 | trait IntoIterator { | ||
963 | type Item; | ||
964 | } | ||
965 | } | ||
966 | //- /alloc.rs crate:alloc deps:core | 1006 | //- /alloc.rs crate:alloc deps:core |
967 | mod collections { | 1007 | mod 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 | ||
986 | pub struct Vec<T> {} | 1025 | pub struct Vec<T> {} |
987 | 1026 | ||
988 | impl<T> Vec<T> { | 1027 | impl<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::*; | ||
1010 | mod iter { | ||
1011 | trait IntoIterator { | ||
1012 | type Item; | ||
1013 | } | ||
1014 | } | ||
1015 | //- /alloc.rs crate:alloc deps:core | 1046 | //- /alloc.rs crate:alloc deps:core |
1016 | mod collections { | 1047 | mod 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 | ||
1041 | pub struct Vec<T> {} | 1071 | pub struct Vec<T> {} |
1042 | 1072 | ||
1043 | impl<T> Vec<T> { | 1073 | impl<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#" | ||
1104 | use core::iter; | ||
1105 | |||
1106 | struct MyIter; | ||
1107 | |||
1108 | impl Iterator for MyIter { | ||
1109 | type Item = (); | ||
1110 | fn next(&mut self) -> Option<Self::Item> { | ||
1111 | None | ||
1112 | } | ||
1113 | } | ||
1114 | |||
1115 | fn 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#" | ||
1141 | use core::iter; | ||
1142 | |||
1143 | struct MyIter; | ||
1144 | |||
1145 | impl Iterator for MyIter { | ||
1146 | type Item = (); | ||
1147 | fn next(&mut self) -> Option<Self::Item> { | ||
1148 | None | ||
1149 | } | ||
1150 | } | ||
1151 | |||
1152 | fn 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 | } |