Use FamousDefs for shorten_iterator hint

This commit is contained in:
Lukas Wirth
2020-10-06 21:05:57 +02:00
parent aaa3905fdd
commit c6f1de6ac5
2 changed files with 85 additions and 69 deletions

View File

@@ -274,24 +274,54 @@ impl TryEnum {
/// somewhat similar to the known paths infra inside hir, but it different; We /// somewhat similar to the known paths infra inside hir, but it different; We
/// want to make sure that IDE specific paths don't become interesting inside /// want to make sure that IDE specific paths don't become interesting inside
/// the compiler itself as well. /// the compiler itself as well.
pub(crate) struct FamousDefs<'a, 'b>(pub(crate) &'a Semantics<'b, RootDatabase>, pub(crate) Crate); pub struct FamousDefs<'a, 'b>(pub &'a Semantics<'b, RootDatabase>, pub Crate);
#[allow(non_snake_case)] #[allow(non_snake_case)]
impl FamousDefs<'_, '_> { impl FamousDefs<'_, '_> {
#[cfg(test)] pub const FIXTURE: &'static str = r#"//- /libcore.rs crate:core
pub(crate) const FIXTURE: &'static str = r#"//- /libcore.rs crate:core
pub mod convert { pub mod convert {
pub trait From<T> { pub trait From<T> {
fn from(T) -> Self; fn from(T) -> Self;
} }
} }
pub mod iter {
pub use self::traits::iterator::Iterator;
mod traits { mod iterator {
use crate::option::Option;
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
} }
pub use self::sources::*;
mod sources {
use super::Iterator;
pub struct Repeat<A> {
element: A,
}
pub fn repeat<T: Clone>(elt: T) -> Repeat<T> {
Repeat { element: elt }
}
impl<A: Clone> Iterator for Repeat<A> {
type Item = A;
fn next(&mut self) -> Option<A> {
Some(self.element.clone())
}
}
}
}
pub mod option { pub mod option {
pub enum Option<T> { None, Some(T)} pub enum Option<T> { None, Some(T)}
} }
pub mod prelude { pub mod prelude {
pub use crate::{convert::From, option::Option::{self, *}}; pub use crate::{convert::From, iter::Iterator, option::Option::{self, *}};
} }
#[prelude_import] #[prelude_import]
pub use prelude::*; pub use prelude::*;
@@ -305,6 +335,10 @@ pub use prelude::*;
self.find_enum("core:option:Option") self.find_enum("core:option:Option")
} }
pub fn core_iter_Iterator(&self) -> Option<Trait> {
self.find_trait("core:iter:traits:iterator:Iterator")
}
fn find_trait(&self, path: &str) -> Option<Trait> { fn find_trait(&self, path: &str) -> Option<Trait> {
match self.find_def(path)? { match self.find_def(path)? {
hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it), hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it),
@@ -324,18 +358,21 @@ pub use prelude::*;
let mut path = path.split(':'); let mut path = path.split(':');
let trait_ = path.next_back()?; let trait_ = path.next_back()?;
let std_crate = path.next()?; let std_crate = path.next()?;
let std_crate = self let std_crate = if self
.1 .1
.dependencies(db) .declaration_name(db)
.into_iter() .map(|name| name.to_string() == std_crate)
.find(|dep| &dep.name.to_string() == std_crate)? .unwrap_or(false)
.krate; {
self.1
} else {
self.1.dependencies(db).into_iter().find(|dep| dep.name.to_string() == std_crate)?.krate
};
let mut module = std_crate.root_module(db); let mut module = std_crate.root_module(db);
for segment in path { for segment in path {
module = module.children(db).find_map(|child| { module = module.children(db).find_map(|child| {
let name = child.name(db)?; let name = child.name(db)?;
if &name.to_string() == segment { if name.to_string() == segment {
Some(child) Some(child)
} else { } else {
None None
@@ -343,7 +380,7 @@ pub use prelude::*;
})?; })?;
} }
let def = let def =
module.scope(db, None).into_iter().find(|(name, _def)| &name.to_string() == trait_)?.1; module.scope(db, None).into_iter().find(|(name, _def)| name.to_string() == trait_)?.1;
Some(def) Some(def)
} }
} }

View File

@@ -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 ide_db::RootDatabase;
use stdx::to_lower_snake_case; use stdx::to_lower_snake_case;
use syntax::{ use syntax::{
@@ -194,7 +195,7 @@ fn get_bind_pat_hints(
} }
let db = sema.db; 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); acc.push(hint);
} else { } else {
acc.push(InlayHint { 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<Item = Ty>`. /// Checks if the type is an Iterator from std::iter and replaces its hint with an `impl Iterator<Item = Ty>`.
fn hint_iterator( fn hint_iterator(
db: &RootDatabase, sema: &Semantics<RootDatabase>,
config: &InlayHintsConfig, config: &InlayHintsConfig,
ty: &Type, ty: &Type,
pat: ast::IdentPat, pat: ast::IdentPat,
) -> Option<InlayHint> { ) -> Option<InlayHint> {
let db = sema.db;
let strukt = ty.as_adt()?; let strukt = ty.as_adt()?;
let krate = strukt.krate(db)?; let krate = strukt.krate(db)?;
let module = strukt.module(db);
if krate.declaration_name(db).as_deref() != Some("core") { if krate.declaration_name(db).as_deref() != Some("core") {
return None; return None;
} }
let module = module // assert this type comes from `core::iter`
strukt
.module(db)
.path_to_root(db) .path_to_root(db)
.into_iter() .into_iter()
.rev() .rev()
.find(|module| module.name(db) == Some(known::iter))?; .find(|module| module.name(db) == Some(known::iter))?;
let iter_trait = module.scope(db, None).into_iter().find_map(|(name, def)| match def { let iter_trait = FamousDefs(sema, krate).core_iter_Iterator()?;
hir::ScopeDef::ModuleDef(ModuleDef::Trait(r#trait)) if name == known::Iterator => {
Some(r#trait)
}
_ => None,
})?;
if ty.impls_trait(db, iter_trait, &[]) { if ty.impls_trait(db, iter_trait, &[]) {
let assoc_type_item = iter_trait.items(db).into_iter().find_map(|item| match item { 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), AssocItem::TypeAlias(alias) if alias.name(db) == known::Item => Some(alias),
_ => None, _ => None,
})?; })?;
if let Some(ty) = ty.normalize_trait_assoc_type(db, iter_trait, &[], assoc_type_item) { if let Some(ty) = ty.normalize_trait_assoc_type(db, iter_trait, &[], assoc_type_item) {
const LABEL_START: &str = "impl Iterator<Item = ";
const LABEL_END: &str = ">";
let ty_display = ty.display_truncated(
db,
config
.max_length
.map(|len| len.saturating_sub(LABEL_START.len() + LABEL_END.len())),
);
return Some(InlayHint { return Some(InlayHint {
range: pat.syntax().text_range(), range: pat.syntax().text_range(),
kind: InlayKind::TypeHint, kind: InlayKind::TypeHint,
label: format!( label: format!("{}{}{}", LABEL_START, ty_display, LABEL_END).into(),
"impl Iterator<Item = {}>",
ty.display_truncated(
db,
config.max_length.map(|len| len - 22 /*len of the template string above*/)
)
)
.into(),
}); });
} }
} }
@@ -401,6 +401,7 @@ fn get_callable(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<Call
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use assists::utils::FamousDefs;
use expect_test::{expect, Expect}; use expect_test::{expect, Expect};
use test_utils::extract_annotations; use test_utils::extract_annotations;
@@ -1124,15 +1125,26 @@ fn main() {
chaining_hints: true, chaining_hints: true,
max_length: None, max_length: None,
}, },
r#" &format!(
"{}\n{}\n",
r#"
//- /main.rs crate:main deps:std //- /main.rs crate:main deps:std
use std::{Option::{self, Some, None}, iter}; use std::{Option::{self, Some, None}, iter};
struct MyIter;
impl iter::Iterator for MyIter {
type Item = ();
fn next(&mut self) -> Option<Self::Item> {
None
}
}
fn main() { fn main() {
let _x = MyIter;
//^^ MyIter
let _x = iter::repeat(0); let _x = iter::repeat(0);
//^^ impl Iterator<Item = i32> //^^ impl Iterator<Item = i32>
let _y = iter::Chain(iter::repeat(0), iter::repeat(0));
//^^ impl Iterator<Item = i32>
fn generic<T: Clone>(t: T) { fn generic<T: Clone>(t: T) {
let _x = iter::repeat(t); let _x = iter::repeat(t);
//^^ impl Iterator<Item = T> //^^ impl Iterator<Item = T>
@@ -1141,42 +1153,9 @@ fn main() {
//- /std.rs crate:std deps:core //- /std.rs crate:std deps:core
use core::*; use core::*;
//- /core.rs crate:core
pub enum Option<T> {
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<T: Clone>(pub T);
pub fn repeat<T: Clone>(t: T) -> Repeat<T> {
Repeat(f)
}
impl<T: Clone> Iterator for Repeat<T> {
type Item = T;
}
pub struct Chain<A, B>(pub A, pub B);
impl<T, A, B> Iterator for Chain<A, B> where A: Iterator<Item = T>, B: Iterator<Item = T> {
type Item = T;
}
}
}
"#, "#,
FamousDefs::FIXTURE
),
); );
} }
} }