Skip to content

Commit aaa3905

Browse files
committed
Shorten type hints for std::iter Iterators
1 parent ec1f459 commit aaa3905

File tree

4 files changed

+162
-9
lines changed

4 files changed

+162
-9
lines changed

crates/hir/src/code_model.rs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,12 @@ use hir_expand::{
3030
use hir_ty::{
3131
autoderef,
3232
display::{HirDisplayError, HirFormatter},
33-
method_resolution, ApplicationTy, CallableDefId, Canonical, FnSig, GenericPredicate,
34-
InEnvironment, Substs, TraitEnvironment, Ty, TyDefId, TypeCtor,
33+
method_resolution,
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,
3539
};
3640
use rustc_hash::FxHashSet;
3741
use stdx::impl_from;
@@ -1362,6 +1366,35 @@ impl Type {
13621366
db.trait_solve(self.krate, goal).is_some()
13631367
}
13641368

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+
13651398
pub fn is_copy(&self, db: &dyn HirDatabase) -> bool {
13661399
let lang_item = db.lang_item(self.krate, SmolStr::new("copy"));
13671400
let copy_trait = match lang_item {

crates/hir/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ pub use hir_def::{
5555
type_ref::{Mutability, TypeRef},
5656
};
5757
pub use hir_expand::{
58-
name::AsName, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc,
58+
name::known, name::AsName, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc,
5959
/* FIXME */ MacroDefId, MacroFile, Origin,
6060
};
6161
pub use hir_ty::display::HirDisplay;

crates/hir_expand/src/name.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ pub mod known {
164164
result,
165165
boxed,
166166
// Components of known path (type name)
167+
Iterator,
167168
IntoIterator,
168169
Item,
169170
Try,

crates/ide/src/inlay_hints.rs

Lines changed: 125 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use hir::{Adt, Callable, HirDisplay, Semantics, Type};
1+
use hir::{known, Adt, AssocItem, Callable, HirDisplay, ModuleDef, Semantics, Type};
22
use ide_db::RootDatabase;
33
use stdx::to_lower_snake_case;
44
use syntax::{
@@ -193,14 +193,68 @@ fn get_bind_pat_hints(
193193
return None;
194194
}
195195

196-
acc.push(InlayHint {
197-
range: pat.syntax().text_range(),
198-
kind: InlayKind::TypeHint,
199-
label: ty.display_truncated(sema.db, config.max_length).to_string().into(),
200-
});
196+
let db = sema.db;
197+
if let Some(hint) = hint_iterator(db, config, &ty, pat.clone()) {
198+
acc.push(hint);
199+
} else {
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+
201207
Some(())
202208
}
203209

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+
204258
fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &Type) -> bool {
205259
if let Some(Adt::Enum(enum_data)) = pat_ty.as_adt() {
206260
let pat_text = bind_pat.to_string();
@@ -1057,6 +1111,71 @@ fn main() {
10571111
let _v = Vec::<Box<dyn Display + Sync>>::new();
10581112
//^^ Vec<Box<dyn Display + Sync>>
10591113
}
1114+
"#,
1115+
);
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+
}
10601179
"#,
10611180
);
10621181
}

0 commit comments

Comments
 (0)