Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions crates/hir-def/src/lang_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,9 @@ pub fn crate_notable_traits(db: &dyn SourceDatabase, krate: Crate) -> Option<Box
let mut traits = Vec::new();

let crate_def_map = crate_def_map(db, krate);
if !crate_def_map.features().doc_notable_trait {
return None;
}

for (_, module_data) in crate_def_map.modules() {
for def in module_data.scope.declarations() {
Expand All @@ -336,6 +339,28 @@ pub fn crate_notable_traits(db: &dyn SourceDatabase, krate: Crate) -> Option<Box
if traits.is_empty() { None } else { Some(traits.into_iter().collect()) }
}

#[salsa::tracked(returns(as_deref))]

@ChayimFriedman2 ChayimFriedman2 Aug 26, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should IMO be the same query with crate_notable_traits(). Also both should check the presence of the unsafe feature like we do for crate_lang_items().

View changes since the review

pub fn crate_auto_traits(db: &dyn SourceDatabase, krate: Crate) -> Option<Box<[TraitId]>> {
let mut traits = Vec::new();

let crate_def_map = crate_def_map(db, krate);
if !crate_def_map.features().auto_traits {
return None;
}

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 =>
Expand Down
7 changes: 5 additions & 2 deletions crates/hir-def/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down
2 changes: 2 additions & 0 deletions crates/hir-def/src/unstable_features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,6 @@ define_unstable_features! {
deref_patterns,
mut_ref,
type_changing_struct_update,
auto_traits,
doc_notable_trait,
}
8 changes: 8 additions & 0 deletions crates/hir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,14 @@ impl Crate {
.flatten()
}

pub fn auto_traits_in_deps(self, db: &dyn HirDatabase) -> impl Iterator<Item = &TraitId> {
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() }
}
Expand Down
1 change: 1 addition & 0 deletions crates/ide/src/hover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
46 changes: 46 additions & 0 deletions crates/ide/src/hover/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,52 @@ pub(super) fn keyword(
Some(HoverResult { markup, actions })
}

pub(super) fn rpit(

@ChayimFriedman2 ChayimFriedman2 Aug 26, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO this is the wrong way to fix. We should instead do that in display infra, for every printed opaque.

Granted, it'll need more work because we don't currently carry the Option<GenericDefId> required for trait solving (to get trait_environment()) in display.

View changes since the review

@A4-Tacks A4-Tacks Aug 28, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO this is the wrong way to fix. We should instead do that in display infra, for every printed opaque.

I think these are two different features, regarding RPIT and any expressions

fn foo() -> Cell<impl Trait> { Cell::new(2) }
               //^^^^ impl Trait + Send + Sync + ...
               //     impl Trait = i32
fn foo() -> Cell<impl Trait> { Cell::new(2) }
                             //^^^^^^^^^^^^ Cell<i32>
                             //             implement auto traits: Send + ...
fn foo() -> Cell<impl Trait> { Cell::new(2) }
                                       //^ i32
                                       //  implement auto traits: Send + Sync + ...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. I'm not saying to add a clause "implements auto traits" similar to "implements notable traits", only to add this information when displaying opaques.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a feature that is bound to the function return, and the opaque semantics in other places are different, such as in the impl Trait of the function parameter

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a feature that is relevant to any opqaue - RPIT, ATPIT, ....

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm…, On the other hand, the current implementation is simple and very practical (in the stable version)

sema: &Semantics<'_, RootDatabase>,
_config: &HoverConfig<'_>,
token: &SyntaxToken,
edition: Edition,
display_target: DisplayTarget,
) -> Option<HoverResult> {
// FIXME: offer on 'async' kw, outputs async fn desugared rpit
if token.kind() != T![impl] {
return None;
}

let db = sema.db;

let impl_type = token.parent().and_then(ast::ImplTraitType::cast)?;
// FIXME: supports nested rpit, like `Option<impl Trait>` #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();

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_, &[]))
.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));
}
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}`
Expand Down
85 changes: 80 additions & 5 deletions crates/ide/src/hover/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9232,6 +9232,7 @@ fn main() {
fn notable_local() {
check(
r#"
#![feature(doc_notable_trait)]
#[doc(notable_trait)]
trait Notable {
type Assoc;
Expand Down Expand Up @@ -9359,6 +9360,7 @@ fn notable_ranged() {
check_hover_range(
r#"
//- minicore: future, iterator
#![feature(doc_notable_trait)]
struct S;
#[doc(notable_trait)]
trait Notable {}
Expand Down Expand Up @@ -9387,6 +9389,7 @@ fn notable_actions() {
check_actions(
r#"
//- minicore: future, iterator
#![feature(doc_notable_trait)]
struct S;
struct S2;
#[doc(notable_trait)]
Expand All @@ -9406,7 +9409,7 @@ impl Iterator for S {
file_id: FileId(
0,
),
offset: 7,
offset: 38,
},
),
GoToType(
Expand Down Expand Up @@ -9445,8 +9448,8 @@ impl Iterator for S {
file_id: FileId(
0,
),
full_range: 21..59,
focus_range: 49..56,
full_range: 52..90,
focus_range: 80..87,
name: "Notable",
kind: Trait,
description: "trait Notable",
Expand All @@ -9458,8 +9461,8 @@ impl Iterator for S {
file_id: FileId(
0,
),
full_range: 10..20,
focus_range: 17..19,
full_range: 41..51,
focus_range: 48..50,
name: "S2",
kind: Struct,
description: "struct S2",
Expand Down Expand Up @@ -10244,6 +10247,78 @@ fn f<T: UnCompat$0>
);
}

#[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
```
"#]],
);

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]
fn issue_18613() {
check(
Expand Down
2 changes: 1 addition & 1 deletion crates/ide/src/inlay_hints/bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ fn foo<T>() {}
file_id: FileId(
1,
),
range: 470..475,
range: 526..531,
},
),
),
Expand Down
2 changes: 1 addition & 1 deletion crates/ide/src/references.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
"#]],
Expand Down
2 changes: 2 additions & 0 deletions crates/intern/src/symbol/symbols.rs
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,8 @@ define_symbols! {
deref_patterns,
mut_ref,
type_changing_struct_update,
auto_traits,
doc_notable_trait,
RangeMin,
RangeMax,
RangeSub,
Expand Down
2 changes: 2 additions & 0 deletions crates/test-utils/src/minicore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@

#![rustc_coherence_is_core]
#![feature(lang_items)]
#![feature(auto_traits)]
#![feature(doc_notable_trait)]

pub mod marker {
// region:sized
Expand Down