From 061ec05182837fadc9f6e6d3d001abd128100529 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 26 Aug 2026 22:47:17 +0800 Subject: [PATCH 1/3] feat: implement a simple hover auto traits in rpit Example --- ```rust //- minicore: send, unpin fn foo() -> $0impl Trait { &raw const () } ``` -> > ```rust > impl Trait + Unpin > impl Trait = *const () > ``` > > --- > > ```rust > impl > ``` > > --- > > keyword docs --- crates/hir-def/src/lang_item.rs | 19 ++++++++++++ crates/hir-def/src/lib.rs | 7 +++-- crates/hir/src/lib.rs | 8 +++++ crates/ide/src/hover.rs | 1 + crates/ide/src/hover/render.rs | 39 ++++++++++++++++++++++++ crates/ide/src/hover/tests.rs | 54 +++++++++++++++++++++++++++++++++ 6 files changed, 126 insertions(+), 2 deletions(-) diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs index 8aeb54b509ca..cfadbe09e850 100644 --- a/crates/hir-def/src/lang_item.rs +++ b/crates/hir-def/src/lang_item.rs @@ -336,6 +336,25 @@ pub fn crate_notable_traits(db: &dyn SourceDatabase, krate: Crate) -> Option Option> { + let mut traits = Vec::new(); + + let crate_def_map = crate_def_map(db, krate); + + for (_, module_data) in crate_def_map.modules() { + for def in module_data.scope.declarations() { + let ModuleDefId::TraitId(trait_) = def else { continue }; + let sig = crate::signatures::TraitSignature::of(db, trait_); + if sig.flags.contains(crate::signatures::TraitFlags::AUTO) { + traits.push(trait_); + } + } + } + + if traits.is_empty() { None } else { Some(traits.into_iter().collect()) } +} + macro_rules! language_item_table { ( $LangItems:ident => diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index 0712a025b49c..57ea692d8271 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -92,8 +92,11 @@ use crate::{ }; pub use crate::{ - find_path::FindPathConfig, hir::type_ref, item_tree::file_item_tree, - lang_item::crate_notable_traits, signatures::LocalFieldId, + find_path::FindPathConfig, + hir::type_ref, + item_tree::file_item_tree, + lang_item::{crate_auto_traits, crate_notable_traits}, + signatures::LocalFieldId, }; pub use hir_expand::{Intern, Lookup, tt}; diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 8f747e397c87..e97f42f69ec9 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -301,6 +301,14 @@ impl Crate { .flatten() } + pub fn auto_traits_in_deps(self, db: &dyn HirDatabase) -> impl Iterator { + self.id + .transitive_deps(db) + .into_iter() + .filter_map(|krate| hir_def::crate_auto_traits(db, krate)) + .flatten() + } + pub fn root_module(self, db: &dyn HirDatabase) -> Module { Module { id: crate_def_map(db, self.id).root_module_id() } } diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 3d73b5b0f24d..0ec985cdbc7f 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -326,6 +326,7 @@ fn hover_offset( res.extend(definitions); continue; } + res.extend(render::rpit(sema, config, &token, edition, display_target)); let keywords = || render::keyword(sema, config, &token, edition, display_target); let underscore = || { if !is_same_kind { diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index f70783fd3c0c..83f450fbaa28 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -261,6 +261,45 @@ pub(super) fn keyword( Some(HoverResult { markup, actions }) } +pub(super) fn rpit( + sema: &Semantics<'_, RootDatabase>, + _config: &HoverConfig<'_>, + token: &SyntaxToken, + edition: Edition, + display_target: DisplayTarget, +) -> Option { + // FIXME: offer on 'async' kw, outputs async fn desugared rpit + if token.kind() != T![impl] { + return None; + } + + let db = sema.db; + + let impl_type = ast::Type::from(token.parent().and_then(ast::ImplTraitType::cast)?); + // FIXME: supports nested rpit, like `Option` #23237 + let ret_type = impl_type.syntax().parent().and_then(ast::RetType::cast)?; + let func = ret_type.syntax().parent().and_then(ast::Fn::cast)?; + + let mut res = impl_type.to_string(); + + let body = func.body()?; + let ty = sema.type_of_expr(&body.into())?.adjusted(); + + let auto_traits = sema + .scope(impl_type.syntax())? + .krate() + .auto_traits_in_deps(db) + .map(|trait_| Trait::from(*trait_)) + .filter(|trait_| trait_.is_auto(db) && ty.impls_trait(db, *trait_, &[])); + for trait_ in auto_traits { + format_to!(res, " + {}", trait_.name(db).display(db, edition)); + } + format_to!(res, "\n{impl_type} = {}", ty.display(db, display_target)); + + let markup = format!("```rust\n{res}\n```").into(); + Some(HoverResult { markup, ..Default::default() }) +} + /// Returns missing types in a record pattern. /// Only makes sense when there's a rest pattern in the record pattern. /// i.e. `let S {a, ..} = S {a: 1, b: 2}` diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 4fea4468c072..2e2cca3aae2a 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -10244,6 +10244,60 @@ fn f ); } +#[test] +fn rpit_keyword() { + check( + r#" +//- minicore: send, unpin +//- /main.rs crate:main deps:std +trait Trait {} +impl Trait for T {} +fn foo() -> $0impl Trait { + &raw const () +} +//- /libstd.rs crate:std +#[doc(keyword = "impl")] +/// keyword docs +mod impl_keyword {} +"#, + expect![[r#" + *impl* + ```rust + impl Trait + Unpin + impl Trait = *const () + ``` + + --- + + ```rust + impl + ``` + + --- + + keyword docs + "#]], + ); + + check( + r#" +//- minicore: send, unpin +trait Trait {} +impl Trait for T {} +fn foo() -> $0impl Trait { + 2 +} +"#, + expect![[r#" + *impl* + ```rust + impl Trait + Send + Unpin + impl Trait = i32 + ``` + "#]], + ); +} + #[test] fn issue_18613() { check( From ef632646a4e43ec296cc88e9278f7a1f717c5309 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 28 Aug 2026 09:01:57 +0800 Subject: [PATCH 2/3] Check unstable feature to early return --- crates/hir-def/src/lang_item.rs | 6 ++++++ crates/hir-def/src/unstable_features.rs | 2 ++ crates/ide/src/hover/tests.rs | 13 ++++++++----- crates/ide/src/inlay_hints/bounds.rs | 2 +- crates/ide/src/references.rs | 2 +- crates/intern/src/symbol/symbols.rs | 2 ++ crates/test-utils/src/minicore.rs | 2 ++ 7 files changed, 22 insertions(+), 7 deletions(-) diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs index cfadbe09e850..5900aa6cb831 100644 --- a/crates/hir-def/src/lang_item.rs +++ b/crates/hir-def/src/lang_item.rs @@ -322,6 +322,9 @@ pub fn crate_notable_traits(db: &dyn SourceDatabase, krate: Crate) -> Option Option() {} file_id: FileId( 1, ), - range: 470..475, + range: 526..531, }, ), ), diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index caf123fa6809..967b9bd43a4c 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs @@ -600,7 +600,7 @@ fn main() { false, false, expect![[r#" - Some Variant FileId(1) 6735..6767 6760..6764 + Some Variant FileId(1) 6791..6823 6816..6820 FileId(0) 46..50 "#]], diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs index 45b4c274a525..82e3c54e0184 100644 --- a/crates/intern/src/symbol/symbols.rs +++ b/crates/intern/src/symbol/symbols.rs @@ -683,6 +683,8 @@ define_symbols! { deref_patterns, mut_ref, type_changing_struct_update, + auto_traits, + doc_notable_trait, RangeMin, RangeMax, RangeSub, diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index 0d9bb4f92bdc..3da392d4c39c 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -88,6 +88,8 @@ #![rustc_coherence_is_core] #![feature(lang_items)] +#![feature(auto_traits)] +#![feature(doc_notable_trait)] pub mod marker { // region:sized From f1879e321fc270eea6f4e1bd3ec1545edc53fa70 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 30 Aug 2026 00:16:15 +0800 Subject: [PATCH 3/3] Filter explicit auto traits bound --- crates/ide/src/hover/render.rs | 11 +++++++++-- crates/ide/src/hover/tests.rs | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index 83f450fbaa28..e746a4ef758c 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -275,10 +275,16 @@ pub(super) fn rpit( let db = sema.db; - let impl_type = ast::Type::from(token.parent().and_then(ast::ImplTraitType::cast)?); + let impl_type = token.parent().and_then(ast::ImplTraitType::cast)?; // FIXME: supports nested rpit, like `Option` #23237 let ret_type = impl_type.syntax().parent().and_then(ast::RetType::cast)?; let func = ret_type.syntax().parent().and_then(ast::Fn::cast)?; + // XXX: Should be resolve type and iteratively the bound list, not a string + let exists = impl_type + .type_bound_list()? + .bounds() + .filter_map(|it| Some(it.ty()?.to_string())) + .collect_vec(); let mut res = impl_type.to_string(); @@ -290,7 +296,8 @@ pub(super) fn rpit( .krate() .auto_traits_in_deps(db) .map(|trait_| Trait::from(*trait_)) - .filter(|trait_| trait_.is_auto(db) && ty.impls_trait(db, *trait_, &[])); + .filter(|trait_| trait_.is_auto(db) && ty.impls_trait(db, *trait_, &[])) + .filter(|trait_| !exists.iter().any(|it| it == trait_.name(db).as_str())); for trait_ in auto_traits { format_to!(res, " + {}", trait_.name(db).display(db, edition)); } diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index aa1d5a9e7da8..d7f420dd19ad 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -10299,6 +10299,24 @@ fn foo() -> $0impl Trait { ``` "#]], ); + + check( + r#" +//- minicore: send, unpin +trait Trait {} +impl Trait for T {} +fn foo() -> $0impl Trait + Unpin { + 2 +} +"#, + expect![[r#" + *impl* + ```rust + impl Trait + Unpin + Send + impl Trait + Unpin = i32 + ``` + "#]], + ); } #[test]