Skip to content

Commit 1ee88db

Browse files
committed
Auto merge of #14533 - lowr:feat/text-edits-for-inlay-hints, r=Veykril
feat: make inlay hints insertable Part of #13812 This PR implements text edit for inlay hints. When an inlay hint contain text edit, user can "accept" it (e.g. by double-clicking in VS Code) to make the hint actual code (effectively deprecating the hint itself). This PR does not implement auto import despite the original request; text edits only insert qualified types along with necessary punctuation. I feel there are some missing pieces to implement efficient auto import (in particular, type traversal function with early exit) so left it for future work. Even without it, user can use `replace_qualified_name_with_use` assist after accepting the edit to achieve the same result. I implemented for the following inlay hints: - top-level identifier pattern in let statements - top-level identifier pattern in closure parameters - closure return type when its has block body One somewhat strange interaction can be observed when top-level identifier pattern has subpattern: text edit inserts type annotation in different place than the inlay hint. Do we want to allow it or should we not provide text edits for these cases at all? ```rust let a /* inlay hint shown here */ @ (b, c) = foo(); let a @ (b, c) /* text edit inserts types here */ = foo(); ```
2 parents 7501d3b + c978d4b commit 1ee88db

27 files changed

+374
-49
lines changed

crates/hir-ty/src/display.rs

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ pub trait HirDisplay {
150150
&'a self,
151151
db: &'a dyn HirDatabase,
152152
module_id: ModuleId,
153+
allow_opaque: bool,
153154
) -> Result<String, DisplaySourceCodeError> {
154155
let mut result = String::new();
155156
match self.hir_fmt(&mut HirFormatter {
@@ -160,7 +161,7 @@ pub trait HirDisplay {
160161
max_size: None,
161162
omit_verbose_types: false,
162163
closure_style: ClosureStyle::ImplFn,
163-
display_target: DisplayTarget::SourceCode { module_id },
164+
display_target: DisplayTarget::SourceCode { module_id, allow_opaque },
164165
}) {
165166
Ok(()) => {}
166167
Err(HirDisplayError::FmtError) => panic!("Writing to String can't fail!"),
@@ -249,25 +250,34 @@ pub enum DisplayTarget {
249250
Diagnostics,
250251
/// Display types for inserting them in source files.
251252
/// The generated code should compile, so paths need to be qualified.
252-
SourceCode { module_id: ModuleId },
253+
SourceCode { module_id: ModuleId, allow_opaque: bool },
253254
/// Only for test purpose to keep real types
254255
Test,
255256
}
256257

257258
impl DisplayTarget {
258-
fn is_source_code(&self) -> bool {
259+
fn is_source_code(self) -> bool {
259260
matches!(self, Self::SourceCode { .. })
260261
}
261-
fn is_test(&self) -> bool {
262+
263+
fn is_test(self) -> bool {
262264
matches!(self, Self::Test)
263265
}
266+
267+
fn allows_opaque(self) -> bool {
268+
match self {
269+
Self::SourceCode { allow_opaque, .. } => allow_opaque,
270+
_ => true,
271+
}
272+
}
264273
}
265274

266275
#[derive(Debug)]
267276
pub enum DisplaySourceCodeError {
268277
PathNotFound,
269278
UnknownType,
270279
Generator,
280+
OpaqueType,
271281
}
272282

273283
pub enum HirDisplayError {
@@ -768,7 +778,7 @@ impl HirDisplay for Ty {
768778
};
769779
write!(f, "{name}")?;
770780
}
771-
DisplayTarget::SourceCode { module_id } => {
781+
DisplayTarget::SourceCode { module_id, allow_opaque: _ } => {
772782
if let Some(path) = find_path::find_path(
773783
db.upcast(),
774784
ItemInNs::Types((*def_id).into()),
@@ -906,6 +916,11 @@ impl HirDisplay for Ty {
906916
f.end_location_link();
907917
}
908918
TyKind::OpaqueType(opaque_ty_id, parameters) => {
919+
if !f.display_target.allows_opaque() {
920+
return Err(HirDisplayError::DisplaySourceCodeError(
921+
DisplaySourceCodeError::OpaqueType,
922+
));
923+
}
909924
let impl_trait_id = db.lookup_intern_impl_trait_id((*opaque_ty_id).into());
910925
match impl_trait_id {
911926
ImplTraitId::ReturnTypeImplTrait(func, idx) => {
@@ -953,8 +968,14 @@ impl HirDisplay for Ty {
953968
}
954969
}
955970
TyKind::Closure(id, substs) => {
956-
if f.display_target.is_source_code() && f.closure_style != ClosureStyle::ImplFn {
957-
never!("Only `impl Fn` is valid for displaying closures in source code");
971+
if f.display_target.is_source_code() {
972+
if !f.display_target.allows_opaque() {
973+
return Err(HirDisplayError::DisplaySourceCodeError(
974+
DisplaySourceCodeError::OpaqueType,
975+
));
976+
} else if f.closure_style != ClosureStyle::ImplFn {
977+
never!("Only `impl Fn` is valid for displaying closures in source code");
978+
}
958979
}
959980
match f.closure_style {
960981
ClosureStyle::Hide => return write!(f, "{TYPE_HINT_TRUNCATION}"),
@@ -1053,6 +1074,11 @@ impl HirDisplay for Ty {
10531074
}
10541075
TyKind::Alias(AliasTy::Projection(p_ty)) => p_ty.hir_fmt(f)?,
10551076
TyKind::Alias(AliasTy::Opaque(opaque_ty)) => {
1077+
if !f.display_target.allows_opaque() {
1078+
return Err(HirDisplayError::DisplaySourceCodeError(
1079+
DisplaySourceCodeError::OpaqueType,
1080+
));
1081+
}
10561082
let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty.opaque_ty_id.into());
10571083
match impl_trait_id {
10581084
ImplTraitId::ReturnTypeImplTrait(func, idx) => {

crates/hir-ty/src/tests.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
159159
let range = node.as_ref().original_file_range(&db);
160160
if let Some(expected) = types.remove(&range) {
161161
let actual = if display_source {
162-
ty.display_source_code(&db, def.module(&db)).unwrap()
162+
ty.display_source_code(&db, def.module(&db), true).unwrap()
163163
} else {
164164
ty.display_test(&db).to_string()
165165
};
@@ -175,7 +175,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
175175
let range = node.as_ref().original_file_range(&db);
176176
if let Some(expected) = types.remove(&range) {
177177
let actual = if display_source {
178-
ty.display_source_code(&db, def.module(&db)).unwrap()
178+
ty.display_source_code(&db, def.module(&db), true).unwrap()
179179
} else {
180180
ty.display_test(&db).to_string()
181181
};

crates/ide-assists/src/handlers/add_explicit_type.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
6969
return None;
7070
}
7171

72-
let inferred_type = ty.display_source_code(ctx.db(), module.into()).ok()?;
72+
let inferred_type = ty.display_source_code(ctx.db(), module.into(), false).ok()?;
7373
acc.add(
7474
AssistId("add_explicit_type", AssistKind::RefactorRewrite),
7575
format!("Insert explicit type `{inferred_type}`"),

crates/ide-assists/src/handlers/add_return_type.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ pub(crate) fn add_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
2222
if ty.is_unit() {
2323
return None;
2424
}
25-
let ty = ty.display_source_code(ctx.db(), module.into()).ok()?;
25+
let ty = ty.display_source_code(ctx.db(), module.into(), true).ok()?;
2626

2727
acc.add(
2828
AssistId("add_return_type", AssistKind::RefactorRewrite),

crates/ide-assists/src/handlers/extract_function.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1884,7 +1884,7 @@ fn with_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr
18841884
}
18851885

18861886
fn format_type(ty: &hir::Type, ctx: &AssistContext<'_>, module: hir::Module) -> String {
1887-
ty.display_source_code(ctx.db(), module.into()).ok().unwrap_or_else(|| "_".to_string())
1887+
ty.display_source_code(ctx.db(), module.into(), true).ok().unwrap_or_else(|| "_".to_string())
18881888
}
18891889

18901890
fn make_ty(ty: &hir::Type, ctx: &AssistContext<'_>, module: hir::Module) -> ast::Type {

crates/ide-assists/src/handlers/generate_constant.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ pub(crate) fn generate_constant(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
4646
let ty = ctx.sema.type_of_expr(&expr)?;
4747
let scope = ctx.sema.scope(statement.syntax())?;
4848
let constant_module = scope.module();
49-
let type_name = ty.original().display_source_code(ctx.db(), constant_module.into()).ok()?;
49+
let type_name =
50+
ty.original().display_source_code(ctx.db(), constant_module.into(), false).ok()?;
5051
let target = statement.syntax().parent()?.text_range();
5152
let path = constant_token.syntax().ancestors().find_map(ast::Path::cast)?;
5253

crates/ide-assists/src/handlers/generate_enum_variant.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ fn expr_ty(
192192
scope: &hir::SemanticsScope<'_>,
193193
) -> Option<ast::Type> {
194194
let ty = ctx.sema.type_of_expr(&arg).map(|it| it.adjusted())?;
195-
let text = ty.display_source_code(ctx.db(), scope.module().into()).ok()?;
195+
let text = ty.display_source_code(ctx.db(), scope.module().into(), false).ok()?;
196196
Some(make::ty(&text))
197197
}
198198

crates/ide-assists/src/handlers/generate_function.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ fn make_return_type(
438438
Some(ty) if ty.is_unit() => (None, false),
439439
Some(ty) => {
440440
necessary_generic_params.extend(ty.generic_params(ctx.db()));
441-
let rendered = ty.display_source_code(ctx.db(), target_module.into());
441+
let rendered = ty.display_source_code(ctx.db(), target_module.into(), true);
442442
match rendered {
443443
Ok(rendered) => (Some(make::ty(&rendered)), false),
444444
Err(_) => (Some(make::ty_placeholder()), true),
@@ -992,9 +992,9 @@ fn fn_arg_type(
992992
let famous_defs = &FamousDefs(&ctx.sema, ctx.sema.scope(fn_arg.syntax())?.krate());
993993
convert_reference_type(ty.strip_references(), ctx.db(), famous_defs)
994994
.map(|conversion| conversion.convert_type(ctx.db()))
995-
.or_else(|| ty.display_source_code(ctx.db(), target_module.into()).ok())
995+
.or_else(|| ty.display_source_code(ctx.db(), target_module.into(), true).ok())
996996
} else {
997-
ty.display_source_code(ctx.db(), target_module.into()).ok()
997+
ty.display_source_code(ctx.db(), target_module.into(), true).ok()
998998
}
999999
}
10001000

crates/ide-assists/src/handlers/promote_local_to_const.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,13 @@ pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_>)
5757
let local = ctx.sema.to_def(&pat)?;
5858
let ty = ctx.sema.type_of_pat(&pat.into())?.original;
5959

60-
if ty.contains_unknown() || ty.is_closure() {
61-
cov_mark::hit!(promote_lcoal_not_applicable_if_ty_not_inferred);
62-
return None;
63-
}
64-
let ty = ty.display_source_code(ctx.db(), module.into()).ok()?;
60+
let ty = match ty.display_source_code(ctx.db(), module.into(), false) {
61+
Ok(ty) => ty,
62+
Err(_) => {
63+
cov_mark::hit!(promote_local_not_applicable_if_ty_not_inferred);
64+
return None;
65+
}
66+
};
6567

6668
let initializer = let_stmt.initializer()?;
6769
if !is_body_const(&ctx.sema, &initializer) {
@@ -187,7 +189,7 @@ fn foo() {
187189

188190
#[test]
189191
fn not_applicable_unknown_ty() {
190-
cov_mark::check!(promote_lcoal_not_applicable_if_ty_not_inferred);
192+
cov_mark::check!(promote_local_not_applicable_if_ty_not_inferred);
191193
check_assist_not_applicable(
192194
promote_local_to_const,
193195
r"

crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ pub(crate) fn replace_turbofish_with_explicit_type(
5555
let returned_type = match ctx.sema.type_of_expr(&initializer) {
5656
Some(returned_type) if !returned_type.original.contains_unknown() => {
5757
let module = ctx.sema.scope(let_stmt.syntax())?.module();
58-
returned_type.original.display_source_code(ctx.db(), module.into()).ok()?
58+
returned_type.original.display_source_code(ctx.db(), module.into(), false).ok()?
5959
}
6060
_ => {
6161
cov_mark::hit!(fallback_to_turbofish_type_if_type_info_not_available);

0 commit comments

Comments
 (0)