diff options
Diffstat (limited to 'crates/ide')
-rw-r--r-- | crates/ide/src/inlay_hints.rs | 131 |
1 files changed, 125 insertions, 6 deletions
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 3a4dc6a84..d8e67bbd9 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use hir::{Adt, Callable, HirDisplay, Semantics, Type}; | 1 | use hir::{known, Adt, AssocItem, Callable, HirDisplay, ModuleDef, Semantics, Type}; |
2 | use ide_db::RootDatabase; | 2 | use ide_db::RootDatabase; |
3 | use stdx::to_lower_snake_case; | 3 | use stdx::to_lower_snake_case; |
4 | use syntax::{ | 4 | use syntax::{ |
@@ -193,14 +193,68 @@ fn get_bind_pat_hints( | |||
193 | return None; | 193 | return None; |
194 | } | 194 | } |
195 | 195 | ||
196 | acc.push(InlayHint { | 196 | let db = sema.db; |
197 | range: pat.syntax().text_range(), | 197 | if let Some(hint) = hint_iterator(db, config, &ty, pat.clone()) { |
198 | kind: InlayKind::TypeHint, | 198 | acc.push(hint); |
199 | label: ty.display_truncated(sema.db, config.max_length).to_string().into(), | 199 | } else { |
200 | }); | 200 | acc.push(InlayHint { |
201 | range: pat.syntax().text_range(), | ||
202 | kind: InlayKind::TypeHint, | ||
203 | label: ty.display_truncated(db, config.max_length).to_string().into(), | ||
204 | }); | ||
205 | } | ||
206 | |||
201 | Some(()) | 207 | Some(()) |
202 | } | 208 | } |
203 | 209 | ||
210 | /// Checks if the type is an Iterator from std::iter and replaces its hint with an `impl Iterator<Item = Ty>`. | ||
211 | fn hint_iterator( | ||
212 | db: &RootDatabase, | ||
213 | config: &InlayHintsConfig, | ||
214 | ty: &Type, | ||
215 | pat: ast::IdentPat, | ||
216 | ) -> Option<InlayHint> { | ||
217 | let strukt = ty.as_adt()?; | ||
218 | let krate = strukt.krate(db)?; | ||
219 | let module = strukt.module(db); | ||
220 | if krate.declaration_name(db).as_deref() != Some("core") { | ||
221 | return None; | ||
222 | } | ||
223 | let module = module | ||
224 | .path_to_root(db) | ||
225 | .into_iter() | ||
226 | .rev() | ||
227 | .find(|module| module.name(db) == Some(known::iter))?; | ||
228 | let iter_trait = module.scope(db, None).into_iter().find_map(|(name, def)| match def { | ||
229 | hir::ScopeDef::ModuleDef(ModuleDef::Trait(r#trait)) if name == known::Iterator => { | ||
230 | Some(r#trait) | ||
231 | } | ||
232 | _ => None, | ||
233 | })?; | ||
234 | if ty.impls_trait(db, iter_trait, &[]) { | ||
235 | let assoc_type_item = iter_trait.items(db).into_iter().find_map(|item| match item { | ||
236 | AssocItem::TypeAlias(alias) if alias.name(db) == known::Item => Some(alias), | ||
237 | _ => None, | ||
238 | })?; | ||
239 | if let Some(ty) = ty.normalize_trait_assoc_type(db, iter_trait, &[], assoc_type_item) { | ||
240 | return Some(InlayHint { | ||
241 | range: pat.syntax().text_range(), | ||
242 | kind: InlayKind::TypeHint, | ||
243 | label: format!( | ||
244 | "impl Iterator<Item = {}>", | ||
245 | ty.display_truncated( | ||
246 | db, | ||
247 | config.max_length.map(|len| len - 22 /*len of the template string above*/) | ||
248 | ) | ||
249 | ) | ||
250 | .into(), | ||
251 | }); | ||
252 | } | ||
253 | } | ||
254 | |||
255 | None | ||
256 | } | ||
257 | |||
204 | fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &Type) -> bool { | 258 | fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &Type) -> bool { |
205 | if let Some(Adt::Enum(enum_data)) = pat_ty.as_adt() { | 259 | if let Some(Adt::Enum(enum_data)) = pat_ty.as_adt() { |
206 | let pat_text = bind_pat.to_string(); | 260 | let pat_text = bind_pat.to_string(); |
@@ -1060,4 +1114,69 @@ fn main() { | |||
1060 | "#, | 1114 | "#, |
1061 | ); | 1115 | ); |
1062 | } | 1116 | } |
1117 | |||
1118 | #[test] | ||
1119 | fn shorten_iterator_hints() { | ||
1120 | check_with_config( | ||
1121 | InlayHintsConfig { | ||
1122 | parameter_hints: false, | ||
1123 | type_hints: true, | ||
1124 | chaining_hints: true, | ||
1125 | max_length: None, | ||
1126 | }, | ||
1127 | r#" | ||
1128 | //- /main.rs crate:main deps:std | ||
1129 | use std::{Option::{self, Some, None}, iter}; | ||
1130 | |||
1131 | fn main() { | ||
1132 | let _x = iter::repeat(0); | ||
1133 | //^^ impl Iterator<Item = i32> | ||
1134 | let _y = iter::Chain(iter::repeat(0), iter::repeat(0)); | ||
1135 | //^^ impl Iterator<Item = i32> | ||
1136 | fn generic<T: Clone>(t: T) { | ||
1137 | let _x = iter::repeat(t); | ||
1138 | //^^ impl Iterator<Item = T> | ||
1139 | } | ||
1140 | } | ||
1141 | |||
1142 | //- /std.rs crate:std deps:core | ||
1143 | use core::*; | ||
1144 | |||
1145 | //- /core.rs crate:core | ||
1146 | pub enum Option<T> { | ||
1147 | Some(T), | ||
1148 | None | ||
1149 | } | ||
1150 | |||
1151 | pub mod iter { | ||
1152 | pub use self::traits::iterator::Iterator; | ||
1153 | pub mod traits { pub mod iterator { | ||
1154 | pub trait Iterator { | ||
1155 | type Item; | ||
1156 | } | ||
1157 | } } | ||
1158 | |||
1159 | pub use self::sources::*; | ||
1160 | pub mod sources { | ||
1161 | use super::Iterator; | ||
1162 | pub struct Repeat<T: Clone>(pub T); | ||
1163 | |||
1164 | pub fn repeat<T: Clone>(t: T) -> Repeat<T> { | ||
1165 | Repeat(f) | ||
1166 | } | ||
1167 | |||
1168 | impl<T: Clone> Iterator for Repeat<T> { | ||
1169 | type Item = T; | ||
1170 | } | ||
1171 | |||
1172 | pub struct Chain<A, B>(pub A, pub B); | ||
1173 | |||
1174 | impl<T, A, B> Iterator for Chain<A, B> where A: Iterator<Item = T>, B: Iterator<Item = T> { | ||
1175 | type Item = T; | ||
1176 | } | ||
1177 | } | ||
1178 | } | ||
1179 | "#, | ||
1180 | ); | ||
1181 | } | ||
1063 | } | 1182 | } |