diff options
-rw-r--r-- | crates/hir/src/code_model.rs | 37 | ||||
-rw-r--r-- | crates/hir/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/hir_expand/src/name.rs | 1 | ||||
-rw-r--r-- | crates/ide/src/inlay_hints.rs | 131 |
4 files changed, 162 insertions, 9 deletions
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs index c75d46bff..031c91ccf 100644 --- a/crates/hir/src/code_model.rs +++ b/crates/hir/src/code_model.rs | |||
@@ -30,8 +30,12 @@ use hir_expand::{ | |||
30 | use hir_ty::{ | 30 | use hir_ty::{ |
31 | autoderef, | 31 | autoderef, |
32 | display::{HirDisplayError, HirFormatter}, | 32 | display::{HirDisplayError, HirFormatter}, |
33 | method_resolution, ApplicationTy, CallableDefId, Canonical, FnSig, GenericPredicate, | 33 | method_resolution, |
34 | InEnvironment, Substs, TraitEnvironment, Ty, TyDefId, TypeCtor, | 34 | traits::Solution, |
35 | traits::SolutionVariables, | ||
36 | ApplicationTy, BoundVar, CallableDefId, Canonical, DebruijnIndex, FnSig, GenericPredicate, | ||
37 | InEnvironment, Obligation, ProjectionPredicate, ProjectionTy, Substs, TraitEnvironment, Ty, | ||
38 | TyDefId, TyKind, TypeCtor, | ||
35 | }; | 39 | }; |
36 | use rustc_hash::FxHashSet; | 40 | use rustc_hash::FxHashSet; |
37 | use stdx::impl_from; | 41 | use stdx::impl_from; |
@@ -1362,6 +1366,35 @@ impl Type { | |||
1362 | db.trait_solve(self.krate, goal).is_some() | 1366 | db.trait_solve(self.krate, goal).is_some() |
1363 | } | 1367 | } |
1364 | 1368 | ||
1369 | pub fn normalize_trait_assoc_type( | ||
1370 | &self, | ||
1371 | db: &dyn HirDatabase, | ||
1372 | r#trait: Trait, | ||
1373 | args: &[Type], | ||
1374 | alias: TypeAlias, | ||
1375 | ) -> Option<Ty> { | ||
1376 | let subst = Substs::build_for_def(db, r#trait.id) | ||
1377 | .push(self.ty.value.clone()) | ||
1378 | .fill(args.iter().map(|t| t.ty.value.clone())) | ||
1379 | .build(); | ||
1380 | let predicate = ProjectionPredicate { | ||
1381 | projection_ty: ProjectionTy { associated_ty: alias.id, parameters: subst }, | ||
1382 | ty: Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0)), | ||
1383 | }; | ||
1384 | let goal = Canonical { | ||
1385 | value: InEnvironment::new( | ||
1386 | self.ty.environment.clone(), | ||
1387 | Obligation::Projection(predicate), | ||
1388 | ), | ||
1389 | kinds: Arc::new([TyKind::General]), | ||
1390 | }; | ||
1391 | |||
1392 | match db.trait_solve(self.krate, goal)? { | ||
1393 | Solution::Unique(SolutionVariables(subst)) => subst.value.first().cloned(), | ||
1394 | Solution::Ambig(_) => None, | ||
1395 | } | ||
1396 | } | ||
1397 | |||
1365 | pub fn is_copy(&self, db: &dyn HirDatabase) -> bool { | 1398 | pub fn is_copy(&self, db: &dyn HirDatabase) -> bool { |
1366 | let lang_item = db.lang_item(self.krate, SmolStr::new("copy")); | 1399 | let lang_item = db.lang_item(self.krate, SmolStr::new("copy")); |
1367 | let copy_trait = match lang_item { | 1400 | let copy_trait = match lang_item { |
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 171118d98..4094a76cb 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -55,7 +55,7 @@ pub use hir_def::{ | |||
55 | type_ref::{Mutability, TypeRef}, | 55 | type_ref::{Mutability, TypeRef}, |
56 | }; | 56 | }; |
57 | pub use hir_expand::{ | 57 | pub use hir_expand::{ |
58 | name::AsName, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, | 58 | name::known, name::AsName, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, |
59 | /* FIXME */ MacroDefId, MacroFile, Origin, | 59 | /* FIXME */ MacroDefId, MacroFile, Origin, |
60 | }; | 60 | }; |
61 | pub use hir_ty::display::HirDisplay; | 61 | pub use hir_ty::display::HirDisplay; |
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs index a5750d829..63f828707 100644 --- a/crates/hir_expand/src/name.rs +++ b/crates/hir_expand/src/name.rs | |||
@@ -164,6 +164,7 @@ pub mod known { | |||
164 | result, | 164 | result, |
165 | boxed, | 165 | boxed, |
166 | // Components of known path (type name) | 166 | // Components of known path (type name) |
167 | Iterator, | ||
167 | IntoIterator, | 168 | IntoIterator, |
168 | Item, | 169 | Item, |
169 | Try, | 170 | Try, |
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 | } |