Skip to content

Commit

Permalink
Merge pull request #18903 from Veykril/push-mqmworppxuyw
Browse files Browse the repository at this point in the history
Implement implicit sized bound inlay hints
  • Loading branch information
Veykril authored Jan 10, 2025
2 parents 1b52a66 + d4fa92e commit 67fd72d
Show file tree
Hide file tree
Showing 10 changed files with 192 additions and 12 deletions.
4 changes: 2 additions & 2 deletions crates/ide-assists/src/handlers/inline_macro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
pub(crate) fn inline_macro(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let unexpanded = ctx.find_node_at_offset::<ast::MacroCall>()?;
let macro_call = ctx.sema.to_def(&unexpanded)?;
let expanded = ctx.sema.parse_or_expand(macro_call.as_file());
let span_map = ctx.sema.db.expansion_span_map(macro_call.as_macro_file());
let target_crate_id = ctx.sema.file_to_module_def(ctx.file_id())?.krate().into();
let text_range = unexpanded.syntax().text_range();

Expand All @@ -48,6 +46,8 @@ pub(crate) fn inline_macro(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
"Inline macro".to_owned(),
text_range,
|builder| {
let expanded = ctx.sema.parse_or_expand(macro_call.as_file());
let span_map = ctx.sema.db.expansion_span_map(macro_call.as_macro_file());
// Don't call `prettify_macro_expansion()` outside the actual assist action; it does some heavy rowan tree manipulation,
// which can be very costly for big macros when it is done *even without the assist being invoked*.
let expanded = prettify_macro_expansion(ctx.db(), expanded, &span_map, target_crate_id);
Expand Down
4 changes: 4 additions & 0 deletions crates/ide-db/src/famous_defs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ impl FamousDefs<'_, '_> {
self.find_trait("core:marker:Copy")
}

pub fn core_marker_Sized(&self) -> Option<Trait> {
self.find_trait("core:marker:Sized")
}

pub fn core_future_Future(&self) -> Option<Trait> {
self.find_trait("core:future:Future")
}
Expand Down
13 changes: 13 additions & 0 deletions crates/ide/src/inlay_hints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use crate::{navigation_target::TryToNav, FileId};
mod adjustment;
mod bind_pat;
mod binding_mode;
mod bounds;
mod chaining;
mod closing_brace;
mod closure_captures;
Expand Down Expand Up @@ -264,6 +265,7 @@ fn hints(
ast::Type::PathType(path) => lifetime::fn_path_hints(hints, ctx, famous_defs, config, file_id, path),
_ => Some(()),
},
ast::GenericParamList(it) => bounds::hints(hints, famous_defs, config, file_id, it),
_ => Some(()),
}
};
Expand All @@ -273,6 +275,7 @@ fn hints(
pub struct InlayHintsConfig {
pub render_colons: bool,
pub type_hints: bool,
pub sized_bound: bool,
pub discriminant_hints: DiscriminantHints,
pub parameter_hints: bool,
pub generic_parameter_hints: GenericParameterHints,
Expand Down Expand Up @@ -760,6 +763,7 @@ mod tests {
render_colons: false,
type_hints: false,
parameter_hints: false,
sized_bound: false,
generic_parameter_hints: GenericParameterHints {
type_hints: false,
lifetime_hints: false,
Expand Down Expand Up @@ -814,6 +818,15 @@ mod tests {
assert_eq!(expected, actual, "\nExpected:\n{expected:#?}\n\nActual:\n{actual:#?}");
}

#[track_caller]
pub(super) fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {
let (analysis, file_id) = fixture::file(ra_fixture);
let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
let filtered =
inlay_hints.into_iter().map(|hint| (hint.range, hint.label)).collect::<Vec<_>>();
expect.assert_debug_eq(&filtered)
}

/// Computes inlay hints for the fixture, applies all the provided text edits and then runs
/// expect test.
#[track_caller]
Expand Down
152 changes: 152 additions & 0 deletions crates/ide/src/inlay_hints/bounds.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
//! Implementation of trait bound hints.
//!
//! Currently this renders the implied `Sized` bound.
use ide_db::{famous_defs::FamousDefs, FileRange};

use span::EditionedFileId;
use syntax::ast::{self, AstNode, HasTypeBounds};

use crate::{
InlayHint, InlayHintLabel, InlayHintLabelPart, InlayHintPosition, InlayHintsConfig, InlayKind,
TryToNav,
};

pub(super) fn hints(
acc: &mut Vec<InlayHint>,
famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
config: &InlayHintsConfig,
_file_id: EditionedFileId,
params: ast::GenericParamList,
) -> Option<()> {
if !config.sized_bound {
return None;
}

let linked_location =
famous_defs.core_marker_Sized().and_then(|it| it.try_to_nav(sema.db)).map(|it| {
let n = it.call_site();
FileRange { file_id: n.file_id, range: n.focus_or_full_range() }
});

for param in params.type_or_const_params() {
match param {
ast::TypeOrConstParam::Type(type_param) => {
let c = type_param.colon_token().map(|it| it.text_range());
let has_bounds =
type_param.type_bound_list().is_some_and(|it| it.bounds().next().is_some());
acc.push(InlayHint {
range: c.unwrap_or_else(|| type_param.syntax().text_range()),
kind: InlayKind::Type,
label: {
let mut hint = InlayHintLabel::default();
if c.is_none() {
hint.parts.push(InlayHintLabelPart {
text: ": ".to_owned(),
linked_location: None,
tooltip: None,
});
}
hint.parts.push(InlayHintLabelPart {
text: "Sized".to_owned(),
linked_location,
tooltip: None,
});
if has_bounds {
hint.parts.push(InlayHintLabelPart {
text: " +".to_owned(),
linked_location: None,
tooltip: None,
});
}
hint
},
text_edit: None,
position: InlayHintPosition::After,
pad_left: c.is_some(),
pad_right: has_bounds,
resolve_parent: Some(params.syntax().text_range()),
});
}
ast::TypeOrConstParam::Const(_) => (),
}
}

Some(())
}

#[cfg(test)]
mod tests {
use expect_test::expect;

use crate::inlay_hints::InlayHintsConfig;

use crate::inlay_hints::tests::{check_expect, check_with_config, DISABLED_CONFIG};

#[track_caller]
fn check(ra_fixture: &str) {
check_with_config(InlayHintsConfig { sized_bound: true, ..DISABLED_CONFIG }, ra_fixture);
}

#[test]
fn smoke() {
check(
r#"
fn foo<T>() {}
// ^ : Sized
"#,
);
}

#[test]
fn with_colon() {
check(
r#"
fn foo<T:>() {}
// ^ Sized
"#,
);
}

#[test]
fn with_colon_and_bounds() {
check(
r#"
fn foo<T: 'static>() {}
// ^ Sized +
"#,
);
}

#[test]
fn location_works() {
check_expect(
InlayHintsConfig { sized_bound: true, ..DISABLED_CONFIG },
r#"
//- minicore: sized
fn foo<T>() {}
"#,
expect![[r#"
[
(
7..8,
[
": ",
InlayHintLabelPart {
text: "Sized",
linked_location: Some(
FileRangeWrapper {
file_id: FileId(
1,
),
range: 135..140,
},
),
tooltip: "",
},
],
),
]
"#]],
);
}
}
11 changes: 1 addition & 10 deletions crates/ide/src/inlay_hints/chaining.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ mod tests {

use crate::{
fixture,
inlay_hints::tests::{check_with_config, DISABLED_CONFIG, TEST_CONFIG},
inlay_hints::tests::{check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG},
InlayHintsConfig,
};

Expand All @@ -90,15 +90,6 @@ mod tests {
check_with_config(InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG }, ra_fixture);
}

#[track_caller]
pub(super) fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {
let (analysis, file_id) = fixture::file(ra_fixture);
let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
let filtered =
inlay_hints.into_iter().map(|hint| (hint.range, hint.label)).collect::<Vec<_>>();
expect.assert_debug_eq(&filtered)
}

#[track_caller]
pub(super) fn check_expect_clear_loc(
config: InlayHintsConfig,
Expand Down
1 change: 1 addition & 0 deletions crates/ide/src/static_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ impl StaticIndex<'_> {
render_colons: true,
discriminant_hints: crate::DiscriminantHints::Fieldless,
type_hints: true,
sized_bound: false,
parameter_hints: true,
generic_parameter_hints: crate::GenericParameterHints {
type_hints: false,
Expand Down
1 change: 1 addition & 0 deletions crates/rust-analyzer/src/cli/analysis_stats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1051,6 +1051,7 @@ impl flags::AnalysisStats {
&InlayHintsConfig {
render_colons: false,
type_hints: true,
sized_bound: false,
discriminant_hints: ide::DiscriminantHints::Always,
parameter_hints: true,
generic_parameter_hints: ide::GenericParameterHints {
Expand Down
3 changes: 3 additions & 0 deletions crates/rust-analyzer/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,8 @@ config_data! {
inlayHints_genericParameterHints_type_enable: bool = false,
/// Whether to show implicit drop hints.
inlayHints_implicitDrops_enable: bool = false,
/// Whether to show inlay hints for the implied type parameter `Sized` bound.
inlayHints_implicitSizedBoundHints_enable: bool = false,
/// Whether to show inlay type hints for elided lifetimes in function signatures.
inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = LifetimeElisionDef::Never,
/// Whether to prefer using parameter names as the name for elided lifetime hints if possible.
Expand Down Expand Up @@ -1621,6 +1623,7 @@ impl Config {
InlayHintsConfig {
render_colons: self.inlayHints_renderColons().to_owned(),
type_hints: self.inlayHints_typeHints_enable().to_owned(),
sized_bound: self.inlayHints_implicitSizedBoundHints_enable().to_owned(),
parameter_hints: self.inlayHints_parameterHints_enable().to_owned(),
generic_parameter_hints: GenericParameterHints {
type_hints: self.inlayHints_genericParameterHints_type_enable().to_owned(),
Expand Down
5 changes: 5 additions & 0 deletions docs/user/generated_config.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,11 @@ Whether to show generic type parameter name inlay hints.
--
Whether to show implicit drop hints.
--
[[rust-analyzer.inlayHints.implicitSizedBoundHints.enable]]rust-analyzer.inlayHints.implicitSizedBoundHints.enable (default: `false`)::
+
--
Whether to show inlay hints for the implied type parameter `Sized` bound.
--
[[rust-analyzer.inlayHints.lifetimeElisionHints.enable]]rust-analyzer.inlayHints.lifetimeElisionHints.enable (default: `"never"`)::
+
--
Expand Down
10 changes: 10 additions & 0 deletions editors/code/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2105,6 +2105,16 @@
}
}
},
{
"title": "inlayHints",
"properties": {
"rust-analyzer.inlayHints.implicitSizedBoundHints.enable": {
"markdownDescription": "Whether to show inlay hints for the implied type parameter `Sized` bound.",
"default": false,
"type": "boolean"
}
}
},
{
"title": "inlayHints",
"properties": {
Expand Down

0 comments on commit 67fd72d

Please sign in to comment.