diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs index 2fd2f18ee4..d25d41c2cf 100644 --- a/crates/hir-def/src/db.rs +++ b/crates/hir-def/src/db.rs @@ -200,7 +200,7 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast Attrs; #[salsa::transparent] - #[salsa::invoke(lang_item::lang_attr_query)] + #[salsa::invoke(lang_item::lang_attr)] fn lang_attr(&self, def: AttrDefId) -> Option; // endregion:attrs @@ -228,6 +228,11 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast Option>; + #[salsa::invoke(crate::lang_item::notable_traits_in_deps)] + fn notable_traits_in_deps(&self, krate: CrateId) -> Arc<[Arc<[TraitId]>]>; + #[salsa::invoke(crate::lang_item::crate_notable_traits)] + fn crate_notable_traits(&self, krate: CrateId) -> Option>; + fn crate_supports_no_std(&self, crate_id: CrateId) -> bool; } diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs index 45ba939b8c..adb2d78543 100644 --- a/crates/hir-def/src/lang_item.rs +++ b/crates/hir-def/src/lang_item.rs @@ -184,17 +184,53 @@ impl LangItems { T: Into + Copy, { let _p = profile::span("collect_lang_item"); - if let Some(lang_item) = db.lang_attr(item.into()) { + if let Some(lang_item) = lang_attr(db, item.into()) { self.items.entry(lang_item).or_insert_with(|| constructor(item)); } } } -pub(crate) fn lang_attr_query(db: &dyn DefDatabase, item: AttrDefId) -> Option { +pub(crate) fn lang_attr(db: &dyn DefDatabase, item: AttrDefId) -> Option { let attrs = db.attrs(item); attrs.by_key("lang").string_value().and_then(|it| LangItem::from_str(&it)) } +pub(crate) fn notable_traits_in_deps( + db: &dyn DefDatabase, + krate: CrateId, +) -> Arc<[Arc<[TraitId]>]> { + let _p = profile::span("notable_traits_in_deps").detail(|| format!("{krate:?}")); + let crate_graph = db.crate_graph(); + + Arc::from_iter( + crate_graph.transitive_deps(krate).filter_map(|krate| db.crate_notable_traits(krate)), + ) +} + +pub(crate) fn crate_notable_traits(db: &dyn DefDatabase, krate: CrateId) -> Option> { + let _p = profile::span("crate_notable_traits").detail(|| format!("{krate:?}")); + + let mut traits = Vec::new(); + + let crate_def_map = db.crate_def_map(krate); + + for (_, module_data) in crate_def_map.modules() { + for def in module_data.scope.declarations() { + if let ModuleDefId::TraitId(trait_) = def { + if db.attrs(trait_.into()).has_doc_notable_trait() { + traits.push(trait_); + } + } + } + } + + if traits.is_empty() { + None + } else { + Some(traits.into_iter().collect()) + } +} + pub enum GenericRequirement { None, Minimum(usize), diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index ab42cbeecc..9866393e54 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -2844,6 +2844,7 @@ impl AssocItem { AssocItem::TypeAlias(it) => Some(it.name(db)), } } + pub fn module(self, db: &dyn HirDatabase) -> Module { match self { AssocItem::Function(f) => f.module(db), @@ -2851,6 +2852,7 @@ impl AssocItem { AssocItem::TypeAlias(t) => t.module(db), } } + pub fn container(self, db: &dyn HirDatabase) -> AssocItemContainer { let container = match self { AssocItem::Function(it) => it.id.lookup(db.upcast()).container, @@ -2886,6 +2888,27 @@ impl AssocItem { AssocItemContainer::Impl(i) => i.trait_(db), } } + + pub fn as_function(self) -> Option { + match self { + Self::Function(v) => Some(v), + _ => None, + } + } + + pub fn as_const(self) -> Option { + match self { + Self::Const(v) => Some(v), + _ => None, + } + } + + pub fn as_type_alias(self) -> Option { + match self { + Self::TypeAlias(v) => Some(v), + _ => None, + } + } } impl HasVisibility for AssocItem { @@ -3024,6 +3047,7 @@ impl LocalSource { impl Local { pub fn is_param(self, db: &dyn HirDatabase) -> bool { + // FIXME: This parses! let src = self.primary_source(db); match src.source.value { Either::Left(pat) => pat diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs index 8f55f30a2d..5995b318e8 100644 --- a/crates/ide-db/src/defs.rs +++ b/crates/ide-db/src/defs.rs @@ -230,23 +230,15 @@ impl Definition { Definition::BuiltinType(it) => it.name().display(db).to_string(), Definition::Local(it) => { let ty = it.ty(db); - let ty = ty.display_truncated(db, None); + let ty_display = ty.display_truncated(db, None); let is_mut = if it.is_mut(db) { "mut " } else { "" }; - let desc = match it.primary_source(db).into_ident_pat() { - Some(ident) => { - let name = it.name(db); - let let_kw = if ident.syntax().parent().map_or(false, |p| { - p.kind() == SyntaxKind::LET_STMT || p.kind() == SyntaxKind::LET_EXPR - }) { - "let " - } else { - "" - }; - format!("{let_kw}{is_mut}{}: {ty}", name.display(db)) - } - None => format!("{is_mut}self: {ty}"), - }; - desc + if it.is_self(db) { + format!("{is_mut}self: {ty_display}") + } else { + let name = it.name(db); + let let_kw = if it.is_param(db) { "" } else { "let " }; + format!("{let_kw}{is_mut}{}: {ty_display}", name.display(db)) + } } Definition::SelfType(impl_def) => { impl_def.self_ty(db).as_adt().and_then(|adt| Definition::Adt(adt).label(db))? diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index 9d449bd18e..d3a9975b2b 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -1,7 +1,10 @@ //! Logic for rendering the different hover messages +use std::{mem, ops::Not}; + use either::Either; use hir::{ - Adt, AsAssocItem, CaptureKind, HasSource, HirDisplay, Layout, LayoutError, Semantics, TypeInfo, + db::DefDatabase, Adt, AsAssocItem, AssocItem, CaptureKind, HasCrate, HasSource, HirDisplay, + Layout, LayoutError, Semantics, TypeInfo, }; use ide_db::{ base_db::SourceDatabase, @@ -390,7 +393,6 @@ pub(super) fn definition( let mod_path = definition_mod_path(db, &def); let label = def.label(db)?; let docs = def.docs(db, famous_defs); - let value = match def { Definition::Variant(it) => { if !it.parent_enum(db).is_data_carrying(db) { @@ -462,14 +464,75 @@ pub(super) fn definition( _ => None, }; - let label = match (value, layout_info) { - (Some(value), Some(layout_info)) => format!("{layout_info}\n{label} = {value}"), - (Some(value), None) => format!("{label} = {value}"), - (None, Some(layout_info)) => format!("{layout_info}\n{label}"), - (None, None) => label, + let def_ty = match def { + Definition::Local(it) => Some(it.ty(db)), + Definition::GenericParam(hir::GenericParam::ConstParam(it)) => Some(it.ty(db)), + Definition::GenericParam(hir::GenericParam::TypeParam(it)) => Some(it.ty(db)), + Definition::Field(field) => Some(field.ty(db)), + Definition::TupleField(it) => Some(it.ty(db)), + Definition::Function(it) => Some(it.ty(db)), + Definition::Adt(it) => Some(it.ty(db)), + Definition::Const(it) => Some(it.ty(db)), + Definition::Static(it) => Some(it.ty(db)), + Definition::TypeAlias(it) => Some(it.ty(db)), + Definition::BuiltinType(it) => Some(it.ty(db)), + _ => None, }; + let notable_traits = def_ty.and_then(|ty| { + let mut desc = String::new(); + let mut needs_impl_header = true; + for &trait_ in db.notable_traits_in_deps(ty.krate(db).into()).iter().flat_map(|it| &**it) { + let trait_ = trait_.into(); + if ty.impls_trait(db, trait_, &[]) { + let aliases: Vec<_> = trait_ + .items(db) + .into_iter() + .filter_map(AssocItem::as_type_alias) + .map(|alias| (ty.normalize_trait_assoc_type(db, &[], alias), alias.name(db))) + .collect(); + desc.push_str(if mem::take(&mut needs_impl_header) { + " // notable traits impls: " + } else { + ", " + }); + format_to!(desc, "{}", trait_.name(db).display(db),); + if !aliases.is_empty() { + desc.push('<'); + format_to!( + desc, + "{}", + aliases.into_iter().format_with(", ", |(ty, name), f| { + f(&name.display(db))?; + f(&" = ")?; + match ty { + Some(ty) => f(&ty.display(db)), + None => f(&"?"), + } + }) + ); + desc.push('>'); + } + } + } + desc.is_empty().not().then(|| desc) + }); - markup(docs.map(Into::into), label, mod_path) + let mut desc = String::new(); + if let Some(notable_traits) = notable_traits { + desc.push_str(¬able_traits); + desc.push('\n'); + } + if let Some(layout_info) = layout_info { + desc.push_str(&layout_info); + desc.push('\n'); + } + desc.push_str(&label); + if let Some(value) = value { + desc.push_str(" = "); + desc.push_str(&value); + } + + markup(docs.map(Into::into), desc, mod_path) } fn type_info( diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 587a6b66c4..f8693cd1ea 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -451,16 +451,16 @@ pub fn foo() -> u32 { 1 } fn main() { let foo_test = fo$0o(); } "#, expect![[r#" - *foo* + *foo* - ```rust - test - ``` + ```rust + test + ``` - ```rust - pub fn foo() -> u32 - ``` - "#]], + ```rust + pub fn foo() -> u32 + ``` + "#]], ); // Use literal `crate` in path @@ -495,16 +495,16 @@ mod m { pub fn foo() -> super::X { super::X } } fn main() { m::f$0oo(); } "#, expect![[r#" - *foo* + *foo* - ```rust - test::m - ``` + ```rust + test::m + ``` - ```rust - pub fn foo() -> super::X - ``` - "#]], + ```rust + pub fn foo() -> super::X + ``` + "#]], ); } @@ -559,18 +559,18 @@ pub fn foo<'a, T: AsRef>(b: &'a T) -> &'a str { } fn main() { let foo_test = fo$0o(); } "#, expect![[r#" - *foo* + *foo* - ```rust - test - ``` + ```rust + test + ``` - ```rust - pub fn foo<'a, T>(b: &'a T) -> &'a str - where - T: AsRef, - ``` - "#]], + ```rust + pub fn foo<'a, T>(b: &'a T) -> &'a str + where + T: AsRef, + ``` + "#]], ); } @@ -583,16 +583,16 @@ pub fn foo$0(a: u32, b: u32) -> u32 {} fn main() { } "#, expect![[r#" - *foo* + *foo* - ```rust - test - ``` + ```rust + test + ``` - ```rust - pub fn foo(a: u32, b: u32) -> u32 - ``` - "#]], + ```rust + pub fn foo(a: u32, b: u32) -> u32 + ``` + "#]], ); } @@ -610,27 +610,27 @@ pub fn foo$0(_: &Path) {} fn main() { } "#, - expect![[r##" - *foo* + expect![[r#" + *foo* - ```rust - test - ``` + ```rust + test + ``` - ```rust - pub fn foo(_: &Path) - ``` + ```rust + pub fn foo(_: &Path) + ``` - --- + --- - # Example + # Example - ``` - # use std::path::Path; - # - foo(Path::new("hello, world!")) - ``` - "##]], + ``` + # use std::path::Path; + # + foo(Path::new("hello, world!")) + ``` + "#]], ); } @@ -643,21 +643,21 @@ pub fn foo$0(_: &Path) {} fn main() { } "##, - expect![[r##" - *foo* + expect![[r#" + *foo* - ```rust - test - ``` + ```rust + test + ``` - ```rust - pub fn foo(_: &Path) - ``` + ```rust + pub fn foo(_: &Path) + ``` - --- + --- - Raw string doc attr - "##]], + Raw string doc attr + "#]], ); } @@ -1040,12 +1040,12 @@ fn hover_for_param_with_multiple_traits() { } fn f(_x$0: impl Deref + DerefMut) {}"#, expect![[r#" - *_x* + *_x* - ```rust - _x: impl Deref + DerefMut - ``` - "#]], + ```rust + _x: impl Deref + DerefMut + ``` + "#]], ) } @@ -1087,15 +1087,15 @@ mod wrapper { fn main() { let foo_test = wrapper::Thing::new$0(); } "#, expect![[r#" - *new* + *new* - ```rust - test::wrapper::Thing - ``` + ```rust + test::wrapper::Thing + ``` - ```rust - pub fn new() -> Thing - ``` + ```rust + pub fn new() -> Thing + ``` "#]], ) } @@ -1299,12 +1299,12 @@ fn test_hover_tuple_field() { check( r#"struct TS(String, i32$0);"#, expect![[r#" - *i32* + *i32* - ```rust - i32 - ``` - "#]], + ```rust + i32 + ``` + "#]], ) } @@ -1319,16 +1319,16 @@ id! { } "#, expect![[r#" - *foo* + *foo* - ```rust - test - ``` + ```rust + test + ``` - ```rust - fn foo() - ``` - "#]], + ```rust + fn foo() + ``` + "#]], ); } @@ -1341,16 +1341,16 @@ fn test_hover_through_attr() { fn foo$0() {} "#, expect![[r#" - *foo* + *foo* - ```rust - test - ``` + ```rust + test + ``` - ```rust - fn foo() - ``` - "#]], + ```rust + fn foo() + ``` + "#]], ); } @@ -1427,16 +1427,16 @@ fn foo() { } "#, expect![[r#" - *bar* + *bar* - ```rust - test - ``` + ```rust + test + ``` - ```rust - fn bar() -> bool - ``` - "#]], + ```rust + fn bar() -> bool + ``` + "#]], ); } @@ -1483,20 +1483,20 @@ fn foo() { } fn bar() { fo$0o(); } ", expect![[r#" - *foo* + *foo* - ```rust - test - ``` + ```rust + test + ``` - ```rust - fn foo() - ``` + ```rust + fn foo() + ``` - --- + --- - \<- ` ` here - "#]], + \<- ` ` here + "#]], ); } @@ -1505,45 +1505,45 @@ fn test_hover_function_show_qualifiers() { check( r#"async fn foo$0() {}"#, expect![[r#" - *foo* + *foo* - ```rust - test - ``` + ```rust + test + ``` - ```rust - async fn foo() - ``` - "#]], + ```rust + async fn foo() + ``` + "#]], ); check( r#"pub const unsafe fn foo$0() {}"#, expect![[r#" - *foo* + *foo* - ```rust - test - ``` + ```rust + test + ``` - ```rust - pub const unsafe fn foo() - ``` - "#]], + ```rust + pub const unsafe fn foo() + ``` + "#]], ); // Top level `pub(crate)` will be displayed as no visibility. check( r#"mod m { pub(crate) async unsafe extern "C" fn foo$0() {} }"#, expect![[r#" - *foo* + *foo* - ```rust - test::m - ``` + ```rust + test::m + ``` - ```rust - pub(crate) async unsafe extern "C" fn foo() - ``` - "#]], + ```rust + pub(crate) async unsafe extern "C" fn foo() + ``` + "#]], ); } @@ -1552,16 +1552,16 @@ fn test_hover_function_show_types() { check( r#"fn foo$0(a: i32, b:i32) -> i32 { 0 }"#, expect![[r#" - *foo* + *foo* - ```rust - test - ``` + ```rust + test + ``` - ```rust - fn foo(a: i32, b: i32) -> i32 - ``` - "#]], + ```rust + fn foo(a: i32, b: i32) -> i32 + ``` + "#]], ); } @@ -1949,39 +1949,39 @@ fn test_hover_no_links() { pub fn fo$0o() {} "#, expect![[r#" - *foo* + *foo* - ```rust - test - ``` + ```rust + test + ``` - ```rust - pub fn foo() - ``` + ```rust + pub fn foo() + ``` - --- + --- - Test cases: - case 1. bare URL: https://www.example.com/ - case 2. inline URL with title: [example](https://www.example.com/) - case 3. code reference: `Result` - case 4. code reference but miss footnote: `String` - case 5. autolink: http://www.example.com/ - case 6. email address: test@example.com - case 7. reference: example - case 8. collapsed link: example - case 9. shortcut link: example - case 10. inline without URL: example - case 11. reference: foo - case 12. reference: foo - case 13. collapsed link: foo - case 14. shortcut link: foo - case 15. inline without URL: foo - case 16. just escaped text: \[foo\] - case 17. inline link: Foo + Test cases: + case 1. bare URL: https://www.example.com/ + case 2. inline URL with title: [example](https://www.example.com/) + case 3. code reference: `Result` + case 4. code reference but miss footnote: `String` + case 5. autolink: http://www.example.com/ + case 6. email address: test@example.com + case 7. reference: example + case 8. collapsed link: example + case 9. shortcut link: example + case 10. inline without URL: example + case 11. reference: foo + case 12. reference: foo + case 13. collapsed link: foo + case 14. shortcut link: foo + case 15. inline without URL: foo + case 16. just escaped text: \[foo\] + case 17. inline link: Foo - [^example]: https://www.example.com/ - "#]], + [^example]: https://www.example.com/ + "#]], ); } @@ -2091,20 +2091,20 @@ bar!(); fn foo() { let bar = Bar; bar.fo$0o(); } "#, expect![[r#" - *foo* + *foo* - ```rust - test::Bar - ``` + ```rust + test::Bar + ``` - ```rust - fn foo(&self) - ``` + ```rust + fn foo(&self) + ``` - --- + --- - Do the foo - "#]], + Do the foo + "#]], ); } @@ -2129,20 +2129,20 @@ bar!(); fn foo() { let bar = Bar; bar.fo$0o(); } "#, expect![[r#" - *foo* + *foo* - ```rust - test::Bar - ``` + ```rust + test::Bar + ``` - ```rust - fn foo(&self) - ``` + ```rust + fn foo(&self) + ``` - --- + --- - Do the foo - "#]], + Do the foo + "#]], ); } @@ -3305,7 +3305,7 @@ fn main() { let foo_test = name_with_dashes::wrapper::Thing::new$0(); } ```rust pub fn new() -> Thing ``` - "#]], + "#]], ) } @@ -3327,7 +3327,7 @@ fn main() { ```rust // size = 8, align = 8, niches = 1 - f: &i32 + let f: &i32 ``` --- @@ -3628,24 +3628,24 @@ fn hover_doc_block_style_indent_end() { fn foo$0() {} "#, expect![[r#" - *foo* + *foo* - ```rust - test - ``` + ```rust + test + ``` - ```rust - fn foo() - ``` + ```rust + fn foo() + ``` - --- + --- - foo + foo - ```rust - let x = 3; - ``` - "#]], + ```rust + let x = 3; + ``` + "#]], ); } @@ -3704,12 +3704,12 @@ trait TraitB {} impl Foo where T: Sized {} "#, expect![[r#" - *T* + *T* - ```rust - T: TraitA + TraitB - ``` - "#]], + ```rust + T: TraitA + TraitB + ``` + "#]], ); check( r#" @@ -3718,12 +3718,12 @@ struct Foo(T); impl Foo {} "#, expect![[r#" - *T* + *T* - ```rust - T - ``` - "#]], + ```rust + T + ``` + "#]], ); // lifetimes bounds arent being tracked yet check( @@ -3733,12 +3733,12 @@ struct Foo(T); impl Foo {} "#, expect![[r#" - *T* + *T* - ```rust - T - ``` - "#]], + ```rust + T + ``` + "#]], ); } @@ -3753,12 +3753,12 @@ struct Foo(T); impl Foo {} "#, expect![[r#" - *T* + *T* - ```rust - T: Trait - ``` - "#]], + ```rust + T: Trait + ``` + "#]], ); check( r#" @@ -3768,12 +3768,12 @@ struct Foo(T); impl Foo {} "#, expect![[r#" - *T* + *T* - ```rust - T: Trait + ?Sized - ``` - "#]], + ```rust + T: Trait + ?Sized + ``` + "#]], ); } @@ -3788,12 +3788,12 @@ mod type_param_sized_bounds { fn foo() {} "#, expect![[r#" - *T* + *T* - ```rust - T - ``` - "#]], + ```rust + T + ``` + "#]], ); } @@ -3805,12 +3805,12 @@ fn foo() {} fn foo() {} "#, expect![[r#" - *T* + *T* - ```rust - T - ``` - "#]], + ```rust + T + ``` + "#]], ); } @@ -3822,12 +3822,12 @@ fn foo() {} fn foo() {} "#, expect![[r#" - *T* + *T* - ```rust - T: ?Sized - ``` - "#]], + ```rust + T: ?Sized + ``` + "#]], ); } @@ -3840,12 +3840,12 @@ trait Trait {} fn foo() {} "#, expect![[r#" - *T* + *T* - ```rust - T: Trait - ``` - "#]], + ```rust + T: Trait + ``` + "#]], ); } @@ -3858,12 +3858,12 @@ trait Trait {} fn foo() {} "#, expect![[r#" - *T* + *T* - ```rust - T: Trait - ``` - "#]], + ```rust + T: Trait + ``` + "#]], ); } @@ -3876,12 +3876,12 @@ trait Trait {} fn foo() {} "#, expect![[r#" - *T* + *T* - ```rust - T: Trait + ?Sized - ``` - "#]], + ```rust + T: Trait + ?Sized + ``` + "#]], ); } @@ -3893,12 +3893,12 @@ fn foo() {} fn foo() {} "#, expect![[r#" - *T* + *T* - ```rust - T - ``` - "#]], + ```rust + T + ``` + "#]], ); check( r#" @@ -3907,12 +3907,12 @@ trait Trait {} fn foo() {} "#, expect![[r#" - *T* + *T* - ```rust - T: Trait - ``` - "#]], + ```rust + T: Trait + ``` + "#]], ); } } @@ -3947,12 +3947,12 @@ struct Foo; impl Foo {} "#, expect![[r#" - *LEN* + *LEN* - ```rust - const LEN: usize - ``` - "#]], + ```rust + const LEN: usize + ``` + "#]], ); } @@ -4082,20 +4082,20 @@ fn main() { } "#, expect![[r#" - *B* + *B* - ```rust - test - ``` + ```rust + test + ``` - ```rust - const B: bool = true - ``` + ```rust + const B: bool = true + ``` - --- + --- - true - "#]], + true + "#]], ); check( @@ -4119,16 +4119,16 @@ fn main() { } "#, expect![[r#" - *AA* + *AA* - ```rust - test - ``` + ```rust + test + ``` - ```rust - const AA: A = A { i: 5 } - ``` - "#]], + ```rust + const AA: A = A { i: 5 } + ``` + "#]], ); check( @@ -5045,17 +5045,17 @@ const _: &str$0 = ""; } mod prim_str {} "#, expect![[r#" - *str* + *str* - ```rust - str - ``` + ```rust + str + ``` - --- + --- - Docs for prim_str - [`foo`](https://doc.rust-lang.org/nightly/std/keyword.foo.html) - "#]], + Docs for prim_str + [`foo`](https://doc.rust-lang.org/nightly/std/keyword.foo.html) + "#]], ); } @@ -5081,20 +5081,20 @@ fn main() { } "#, expect![[r#" - *bar* + *bar* - ```rust - test - ``` + ```rust + test + ``` - ```rust - fn bar<'t, T>(s: &mut S<'t, T>, t: u32) -> *mut u32 - where - T: Clone + 't, - 't: 't + 't, - for<'a> T: Clone + 'a, - ``` - "#]], + ```rust + fn bar<'t, T>(s: &mut S<'t, T>, t: u32) -> *mut u32 + where + T: Clone + 't, + 't: 't + 't, + for<'a> T: Clone + 'a, + ``` + "#]], ) } @@ -5164,16 +5164,16 @@ impl T1 for Foo { } "#, expect![[r#" -*Bar* + *Bar* -```rust -test::t2 -``` + ```rust + test::t2 + ``` -```rust -pub type Bar -``` -"#]], + ```rust + pub type Bar + ``` + "#]], ); } #[test] @@ -5186,16 +5186,16 @@ trait A { type Assoc; }"#, expect![[r#" - *Assoc* + *Assoc* - ```rust - test - ``` + ```rust + test + ``` - ```rust - type Assoc - ``` - "#]], + ```rust + type Assoc + ``` + "#]], ); check( r#" @@ -5207,16 +5207,16 @@ trait A { type Assoc; }"#, expect![[r#" - *Assoc* + *Assoc* - ```rust - test - ``` + ```rust + test + ``` - ```rust - type Assoc - ``` - "#]], + ```rust + type Assoc + ``` + "#]], ); check( r#" @@ -5226,16 +5226,16 @@ trait A where type Assoc; }"#, expect![[r#" - *Assoc* + *Assoc* - ```rust - test - ``` + ```rust + test + ``` - ```rust - type Assoc - ``` - "#]], + ```rust + type Assoc + ``` + "#]], ); } @@ -7059,3 +7059,63 @@ fn main() { "#]], ); } + +#[test] +fn notable_local() { + check( + r#" +#[doc(notable_trait)] +trait Notable { + type Assoc; + type Assoc2; +} + +impl Notable for u32 { + type Assoc = &str; + type Assoc2 = char; +} +fn main(notable$0: u32) {} +"#, + expect![[r#" + *notable* + + ```rust + // notable traits impls: Notable + // size = 4, align = 4 + notable: u32 + ``` + "#]], + ); +} + +#[test] +fn notable_foreign() { + check( + r#" +//- minicore: future, iterator +struct S; +#[doc(notable_trait)] +trait Notable {} +impl Notable for S$0 {} +impl core::future::Future for S { + type Output = u32; +} +impl Iterator for S { + type Item = S; +} +"#, + expect![[r#" + *S* + + ```rust + test + ``` + + ```rust + // notable traits impls: Notable, Future, Iterator + // size = 0, align = 1 + struct S + ``` + "#]], + ); +} diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index 3b836dfcd9..b015dd69b5 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -1166,6 +1166,7 @@ pub mod future { task::{Context, Poll}, }; + #[doc(notable_trait)] #[lang = "future_trait"] pub trait Future { type Output; @@ -1264,6 +1265,7 @@ pub mod iter { mod traits { mod iterator { + #[doc(notable_trait)] pub trait Iterator { type Item; #[lang = "next"]