From aaa3905fdd18a7981d40ac371099ae9044e833a8 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 6 Oct 2020 19:07:34 +0200 Subject: Shorten type hints for std::iter Iterators --- crates/ide/src/inlay_hints.rs | 131 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 125 insertions(+), 6 deletions(-) (limited to 'crates/ide') 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 @@ -use hir::{Adt, Callable, HirDisplay, Semantics, Type}; +use hir::{known, Adt, AssocItem, Callable, HirDisplay, ModuleDef, Semantics, Type}; use ide_db::RootDatabase; use stdx::to_lower_snake_case; use syntax::{ @@ -193,14 +193,68 @@ fn get_bind_pat_hints( return None; } - acc.push(InlayHint { - range: pat.syntax().text_range(), - kind: InlayKind::TypeHint, - label: ty.display_truncated(sema.db, config.max_length).to_string().into(), - }); + let db = sema.db; + if let Some(hint) = hint_iterator(db, config, &ty, pat.clone()) { + acc.push(hint); + } else { + acc.push(InlayHint { + range: pat.syntax().text_range(), + kind: InlayKind::TypeHint, + label: ty.display_truncated(db, config.max_length).to_string().into(), + }); + } + Some(()) } +/// Checks if the type is an Iterator from std::iter and replaces its hint with an `impl Iterator`. +fn hint_iterator( + db: &RootDatabase, + config: &InlayHintsConfig, + ty: &Type, + pat: ast::IdentPat, +) -> Option { + let strukt = ty.as_adt()?; + let krate = strukt.krate(db)?; + let module = strukt.module(db); + if krate.declaration_name(db).as_deref() != Some("core") { + return None; + } + let module = module + .path_to_root(db) + .into_iter() + .rev() + .find(|module| module.name(db) == Some(known::iter))?; + let iter_trait = module.scope(db, None).into_iter().find_map(|(name, def)| match def { + hir::ScopeDef::ModuleDef(ModuleDef::Trait(r#trait)) if name == known::Iterator => { + Some(r#trait) + } + _ => None, + })?; + if ty.impls_trait(db, iter_trait, &[]) { + let assoc_type_item = iter_trait.items(db).into_iter().find_map(|item| match item { + AssocItem::TypeAlias(alias) if alias.name(db) == known::Item => Some(alias), + _ => None, + })?; + if let Some(ty) = ty.normalize_trait_assoc_type(db, iter_trait, &[], assoc_type_item) { + return Some(InlayHint { + range: pat.syntax().text_range(), + kind: InlayKind::TypeHint, + label: format!( + "impl Iterator", + ty.display_truncated( + db, + config.max_length.map(|len| len - 22 /*len of the template string above*/) + ) + ) + .into(), + }); + } + } + + None +} + fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &Type) -> bool { if let Some(Adt::Enum(enum_data)) = pat_ty.as_adt() { let pat_text = bind_pat.to_string(); @@ -1057,6 +1111,71 @@ fn main() { let _v = Vec::>::new(); //^^ Vec> } +"#, + ); + } + + #[test] + fn shorten_iterator_hints() { + check_with_config( + InlayHintsConfig { + parameter_hints: false, + type_hints: true, + chaining_hints: true, + max_length: None, + }, + r#" +//- /main.rs crate:main deps:std +use std::{Option::{self, Some, None}, iter}; + +fn main() { + let _x = iter::repeat(0); + //^^ impl Iterator + let _y = iter::Chain(iter::repeat(0), iter::repeat(0)); + //^^ impl Iterator + fn generic(t: T) { + let _x = iter::repeat(t); + //^^ impl Iterator + } +} + +//- /std.rs crate:std deps:core +use core::*; + +//- /core.rs crate:core +pub enum Option { + Some(T), + None +} + +pub mod iter { + pub use self::traits::iterator::Iterator; + pub mod traits { pub mod iterator { + pub trait Iterator { + type Item; + } + } } + + pub use self::sources::*; + pub mod sources { + use super::Iterator; + pub struct Repeat(pub T); + + pub fn repeat(t: T) -> Repeat { + Repeat(f) + } + + impl Iterator for Repeat { + type Item = T; + } + + pub struct Chain(pub A, pub B); + + impl Iterator for Chain where A: Iterator, B: Iterator { + type Item = T; + } + } +} "#, ); } -- cgit v1.2.3 From c6f1de6ac5d3496fc3c30b5e15263db68d057695 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 6 Oct 2020 21:05:57 +0200 Subject: Use FamousDefs for shorten_iterator hint --- crates/ide/src/inlay_hints.rs | 93 +++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 57 deletions(-) (limited to 'crates/ide') diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index d8e67bbd9..27bd1e37f 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -1,4 +1,5 @@ -use hir::{known, Adt, AssocItem, Callable, HirDisplay, ModuleDef, Semantics, Type}; +use assists::utils::FamousDefs; +use hir::{known, Adt, AssocItem, Callable, HirDisplay, Semantics, Type}; use ide_db::RootDatabase; use stdx::to_lower_snake_case; use syntax::{ @@ -194,7 +195,7 @@ fn get_bind_pat_hints( } let db = sema.db; - if let Some(hint) = hint_iterator(db, config, &ty, pat.clone()) { + if let Some(hint) = hint_iterator(sema, config, &ty, pat.clone()) { acc.push(hint); } else { acc.push(InlayHint { @@ -209,45 +210,44 @@ fn get_bind_pat_hints( /// Checks if the type is an Iterator from std::iter and replaces its hint with an `impl Iterator`. fn hint_iterator( - db: &RootDatabase, + sema: &Semantics, config: &InlayHintsConfig, ty: &Type, pat: ast::IdentPat, ) -> Option { + let db = sema.db; let strukt = ty.as_adt()?; let krate = strukt.krate(db)?; - let module = strukt.module(db); if krate.declaration_name(db).as_deref() != Some("core") { return None; } - let module = module + // assert this type comes from `core::iter` + strukt + .module(db) .path_to_root(db) .into_iter() .rev() .find(|module| module.name(db) == Some(known::iter))?; - let iter_trait = module.scope(db, None).into_iter().find_map(|(name, def)| match def { - hir::ScopeDef::ModuleDef(ModuleDef::Trait(r#trait)) if name == known::Iterator => { - Some(r#trait) - } - _ => None, - })?; + let iter_trait = FamousDefs(sema, krate).core_iter_Iterator()?; if ty.impls_trait(db, iter_trait, &[]) { let assoc_type_item = iter_trait.items(db).into_iter().find_map(|item| match item { AssocItem::TypeAlias(alias) if alias.name(db) == known::Item => Some(alias), _ => None, })?; if let Some(ty) = ty.normalize_trait_assoc_type(db, iter_trait, &[], assoc_type_item) { + const LABEL_START: &str = "impl Iterator", - ty.display_truncated( - db, - config.max_length.map(|len| len - 22 /*len of the template string above*/) - ) - ) - .into(), + label: format!("{}{}{}", LABEL_START, ty_display, LABEL_END).into(), }); } } @@ -401,6 +401,7 @@ fn get_callable(sema: &Semantics, expr: &ast::Expr) -> Option Option { + None + } +} + fn main() { + let _x = MyIter; + //^^ MyIter let _x = iter::repeat(0); //^^ impl Iterator - let _y = iter::Chain(iter::repeat(0), iter::repeat(0)); - //^^ impl Iterator fn generic(t: T) { let _x = iter::repeat(t); //^^ impl Iterator @@ -1141,42 +1153,9 @@ fn main() { //- /std.rs crate:std deps:core use core::*; - -//- /core.rs crate:core -pub enum Option { - Some(T), - None -} - -pub mod iter { - pub use self::traits::iterator::Iterator; - pub mod traits { pub mod iterator { - pub trait Iterator { - type Item; - } - } } - - pub use self::sources::*; - pub mod sources { - use super::Iterator; - pub struct Repeat(pub T); - - pub fn repeat(t: T) -> Repeat { - Repeat(f) - } - - impl Iterator for Repeat { - type Item = T; - } - - pub struct Chain(pub A, pub B); - - impl Iterator for Chain where A: Iterator, B: Iterator { - type Item = T; - } - } -} "#, + FamousDefs::FIXTURE + ), ); } } -- cgit v1.2.3 From c133651e0a613d4833bba1c1f229222d060e2ba8 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 7 Oct 2020 10:14:42 +0200 Subject: Move IntoIterator into FamousDefs --- crates/ide/src/inlay_hints.rs | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) (limited to 'crates/ide') diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 27bd1e37f..31a6a1be8 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -412,7 +412,8 @@ mod tests { } fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) { - let (analysis, file_id) = fixture::file(ra_fixture); + let ra_fixture = format!("{}\n{}", ra_fixture, FamousDefs::FIXTURE); + let (analysis, file_id) = fixture::file(&ra_fixture); let expected = extract_annotations(&*analysis.file_text(file_id).unwrap()); let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap(); let actual = @@ -1011,13 +1012,6 @@ fn main() { println!("Unit expr"); } -//- /core.rs crate:core -#[prelude_import] use iter::*; -mod iter { - trait IntoIterator { - type Item; - } -} //- /alloc.rs crate:alloc deps:core mod collections { struct Vec {} @@ -1059,14 +1053,6 @@ fn main() { //^ &str } } - -//- /core.rs crate:core -#[prelude_import] use iter::*; -mod iter { - trait IntoIterator { - type Item; - } -} //- /alloc.rs crate:alloc deps:core mod collections { struct Vec {} @@ -1125,15 +1111,13 @@ fn main() { chaining_hints: true, max_length: None, }, - &format!( - "{}\n{}\n", - r#" + r#" //- /main.rs crate:main deps:std -use std::{Option::{self, Some, None}, iter}; +use std::iter; struct MyIter; -impl iter::Iterator for MyIter { +impl Iterator for MyIter { type Item = (); fn next(&mut self) -> Option { None @@ -1154,8 +1138,6 @@ fn main() { //- /std.rs crate:std deps:core use core::*; "#, - FamousDefs::FIXTURE - ), ); } } -- cgit v1.2.3 From 209e9b9926d27ac71bc054bfdd48888e5d7d6d1a Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 7 Oct 2020 11:30:42 +0200 Subject: Shorten iterator chain hints --- crates/ide/src/inlay_hints.rs | 117 +++++++++++++++++++++++++++++------------- 1 file changed, 81 insertions(+), 36 deletions(-) (limited to 'crates/ide') diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 31a6a1be8..279d02541 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -126,11 +126,12 @@ fn get_chaining_hints( } } } - let label = ty.display_truncated(sema.db, config.max_length).to_string(); acc.push(InlayHint { range: expr.syntax().text_range(), kind: InlayKind::ChainingHint, - label: label.into(), + label: hint_iterator(sema, config, &ty).unwrap_or_else(|| { + ty.display_truncated(sema.db, config.max_length).to_string().into() + }), }); } Some(()) @@ -193,17 +194,12 @@ fn get_bind_pat_hints( if should_not_display_type_hint(sema, &pat, &ty) { return None; } - - let db = sema.db; - if let Some(hint) = hint_iterator(sema, config, &ty, pat.clone()) { - acc.push(hint); - } else { - acc.push(InlayHint { - range: pat.syntax().text_range(), - kind: InlayKind::TypeHint, - label: ty.display_truncated(db, config.max_length).to_string().into(), - }); - } + acc.push(InlayHint { + range: pat.syntax().text_range(), + kind: InlayKind::TypeHint, + label: hint_iterator(sema, config, &ty) + .unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string().into()), + }); Some(()) } @@ -213,8 +209,7 @@ fn hint_iterator( sema: &Semantics, config: &InlayHintsConfig, ty: &Type, - pat: ast::IdentPat, -) -> Option { +) -> Option { let db = sema.db; let strukt = ty.as_adt()?; let krate = strukt.krate(db)?; @@ -244,11 +239,7 @@ fn hint_iterator( .max_length .map(|len| len.saturating_sub(LABEL_START.len() + LABEL_END.len())), ); - return Some(InlayHint { - range: pat.syntax().text_range(), - kind: InlayKind::TypeHint, - label: format!("{}{}{}", LABEL_START, ty_display, LABEL_END).into(), - }); + return Some(format!("{}{}{}", LABEL_START, ty_display, LABEL_END).into()); } } @@ -412,7 +403,8 @@ mod tests { } fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) { - let ra_fixture = format!("{}\n{}", ra_fixture, FamousDefs::FIXTURE); + let ra_fixture = + format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE); let (analysis, file_id) = fixture::file(&ra_fixture); let expected = extract_annotations(&*analysis.file_text(file_id).unwrap()); let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap(); @@ -422,7 +414,9 @@ mod tests { } fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) { - let (analysis, file_id) = fixture::file(ra_fixture); + let ra_fixture = + format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE); + let (analysis, file_id) = fixture::file(&ra_fixture); let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap(); expect.assert_debug_eq(&inlay_hints) } @@ -854,12 +848,12 @@ fn main() { expect![[r#" [ InlayHint { - range: 147..172, + range: 148..173, kind: ChainingHint, label: "B", }, InlayHint { - range: 147..154, + range: 148..155, kind: ChainingHint, label: "A", }, @@ -920,12 +914,12 @@ fn main() { expect![[r#" [ InlayHint { - range: 143..190, + range: 144..191, kind: ChainingHint, label: "C", }, InlayHint { - range: 143..179, + range: 144..180, kind: ChainingHint, label: "B", }, @@ -965,12 +959,12 @@ fn main() { expect![[r#" [ InlayHint { - range: 246..283, + range: 247..284, kind: ChainingHint, label: "B>", }, InlayHint { - range: 246..265, + range: 247..266, kind: ChainingHint, label: "A>", }, @@ -991,7 +985,6 @@ fn main() { ); check( r#" -//- /main.rs crate:main deps:core pub struct Vec {} impl Vec { @@ -1031,7 +1024,6 @@ mod collections { fn complete_for_hint() { check( r#" -//- /main.rs crate:main deps:core pub struct Vec {} impl Vec { @@ -1078,7 +1070,6 @@ mod collections { max_length: None, }, r#" -//- /main.rs crate:main pub struct Vec {} impl Vec { @@ -1108,12 +1099,11 @@ fn main() { InlayHintsConfig { parameter_hints: false, type_hints: true, - chaining_hints: true, + chaining_hints: false, max_length: None, }, r#" -//- /main.rs crate:main deps:std -use std::iter; +use core::iter; struct MyIter; @@ -1132,12 +1122,67 @@ fn main() { fn generic(t: T) { let _x = iter::repeat(t); //^^ impl Iterator + let _chained = iter::repeat(t).take(10); + //^^^^^^^^ impl Iterator + } +} +"#, + ); + } + + #[test] + fn shorten_iterator_chaining_hints() { + check_expect( + InlayHintsConfig { + parameter_hints: false, + type_hints: false, + chaining_hints: true, + max_length: None, + }, + r#" +use core::iter; + +struct MyIter; + +impl Iterator for MyIter { + type Item = (); + fn next(&mut self) -> Option { + None } } -//- /std.rs crate:std deps:core -use core::*; +fn main() { + let _x = MyIter.by_ref() + .take(5) + .by_ref() + .take(5) + .by_ref(); +} "#, + expect![[r#" + [ + InlayHint { + range: 175..242, + kind: ChainingHint, + label: "impl Iterator", + }, + InlayHint { + range: 175..225, + kind: ChainingHint, + label: "&mut Take<&mut MyIter>", + }, + InlayHint { + range: 175..207, + kind: ChainingHint, + label: "impl Iterator", + }, + InlayHint { + range: 175..190, + kind: ChainingHint, + label: "&mut MyIter", + }, + ] + "#]], ); } } -- cgit v1.2.3 From e106857e80d1ea304b00b5ff8a3294ca9bacd959 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 7 Oct 2020 12:13:32 +0200 Subject: Shorten iterator hints for std::iter iterators behind references --- crates/ide/src/inlay_hints.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'crates/ide') diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 279d02541..7540f56a4 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -211,7 +211,9 @@ fn hint_iterator( ty: &Type, ) -> Option { let db = sema.db; - let strukt = ty.as_adt()?; + let strukt = std::iter::successors(Some(ty.clone()), |ty| ty.remove_ref()) + .last() + .and_then(|strukt| strukt.as_adt())?; let krate = strukt.krate(db)?; if krate.declaration_name(db).as_deref() != Some("core") { return None; @@ -1169,7 +1171,7 @@ fn main() { InlayHint { range: 175..225, kind: ChainingHint, - label: "&mut Take<&mut MyIter>", + label: "impl Iterator", }, InlayHint { range: 175..207, -- cgit v1.2.3 From 783af171f74d95b498662e5168c3ba320cca8553 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 7 Oct 2020 13:14:12 +0200 Subject: Clean up inlay_hints --- crates/ide/src/inlay_hints.rs | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) (limited to 'crates/ide') diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 7540f56a4..7d716577e 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -1,5 +1,5 @@ use assists::utils::FamousDefs; -use hir::{known, Adt, AssocItem, Callable, HirDisplay, Semantics, Type}; +use hir::{known, HirDisplay, Semantics}; use ide_db::RootDatabase; use stdx::to_lower_snake_case; use syntax::{ @@ -120,7 +120,7 @@ fn get_chaining_hints( return None; } if matches!(expr, ast::Expr::PathExpr(_)) { - if let Some(Adt::Struct(st)) = ty.as_adt() { + if let Some(hir::Adt::Struct(st)) = ty.as_adt() { if st.fields(sema.db).is_empty() { return None; } @@ -208,7 +208,7 @@ fn get_bind_pat_hints( fn hint_iterator( sema: &Semantics, config: &InlayHintsConfig, - ty: &Type, + ty: &hir::Type, ) -> Option { let db = sema.db; let strukt = std::iter::successors(Some(ty.clone()), |ty| ty.remove_ref()) @@ -218,17 +218,13 @@ fn hint_iterator( if krate.declaration_name(db).as_deref() != Some("core") { return None; } - // assert this type comes from `core::iter` - strukt - .module(db) - .path_to_root(db) - .into_iter() - .rev() - .find(|module| module.name(db) == Some(known::iter))?; let iter_trait = FamousDefs(sema, krate).core_iter_Iterator()?; + let iter_mod = FamousDefs(sema, krate).core_iter()?; + // assert this type comes from `core::iter` + iter_mod.visibility_of(db, &iter_trait.into()).filter(|&vis| vis == hir::Visibility::Public)?; if ty.impls_trait(db, iter_trait, &[]) { let assoc_type_item = iter_trait.items(db).into_iter().find_map(|item| match item { - AssocItem::TypeAlias(alias) if alias.name(db) == known::Item => Some(alias), + hir::AssocItem::TypeAlias(alias) if alias.name(db) == known::Item => Some(alias), _ => None, })?; if let Some(ty) = ty.normalize_trait_assoc_type(db, iter_trait, &[], assoc_type_item) { @@ -248,8 +244,8 @@ fn hint_iterator( None } -fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &Type) -> bool { - if let Some(Adt::Enum(enum_data)) = pat_ty.as_adt() { +fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &hir::Type) -> bool { + if let Some(hir::Adt::Enum(enum_data)) = pat_ty.as_adt() { let pat_text = bind_pat.to_string(); enum_data .variants(db) @@ -264,7 +260,7 @@ fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &Typ fn should_not_display_type_hint( sema: &Semantics, bind_pat: &ast::IdentPat, - pat_ty: &Type, + pat_ty: &hir::Type, ) -> bool { let db = sema.db; @@ -272,7 +268,7 @@ fn should_not_display_type_hint( return true; } - if let Some(Adt::Struct(s)) = pat_ty.as_adt() { + if let Some(hir::Adt::Struct(s)) = pat_ty.as_adt() { if s.fields(db).is_empty() && s.name(db).to_string() == bind_pat.to_string() { return true; } @@ -316,7 +312,7 @@ fn should_not_display_type_hint( fn should_show_param_name_hint( sema: &Semantics, - callable: &Callable, + callable: &hir::Callable, param_name: &str, argument: &ast::Expr, ) -> bool { @@ -363,7 +359,7 @@ fn is_enum_name_similar_to_param_name( param_name: &str, ) -> bool { match sema.type_of_expr(argument).and_then(|t| t.as_adt()) { - Some(Adt::Enum(e)) => to_lower_snake_case(&e.name(sema.db).to_string()) == param_name, + Some(hir::Adt::Enum(e)) => to_lower_snake_case(&e.name(sema.db).to_string()) == param_name, _ => false, } } @@ -384,7 +380,7 @@ fn is_obvious_param(param_name: &str) -> bool { param_name.len() == 1 || is_obvious_param_name } -fn get_callable(sema: &Semantics, expr: &ast::Expr) -> Option { +fn get_callable(sema: &Semantics, expr: &ast::Expr) -> Option { match expr { ast::Expr::CallExpr(expr) => sema.type_of_expr(&expr.expr()?)?.as_callable(sema.db), ast::Expr::MethodCallExpr(expr) => sema.resolve_method_call_as_callable(expr), -- cgit v1.2.3