Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/hir-ty/src/display.rs40
-rw-r--r--crates/hir-ty/src/tests.rs4
-rw-r--r--crates/ide-assists/src/handlers/add_explicit_type.rs2
-rw-r--r--crates/ide-assists/src/handlers/add_return_type.rs2
-rw-r--r--crates/ide-assists/src/handlers/extract_function.rs2
-rw-r--r--crates/ide-assists/src/handlers/generate_constant.rs3
-rw-r--r--crates/ide-assists/src/handlers/generate_enum_variant.rs2
-rw-r--r--crates/ide-assists/src/handlers/generate_function.rs6
-rw-r--r--crates/ide-assists/src/handlers/promote_local_to_const.rs14
-rw-r--r--crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs2
-rw-r--r--crates/ide-completion/src/completions/fn_param.rs2
-rw-r--r--crates/ide-completion/src/completions/type.rs2
-rw-r--r--crates/ide-db/src/path_transform.rs4
-rw-r--r--crates/ide-diagnostics/src/handlers/missing_fields.rs4
-rw-r--r--crates/ide-diagnostics/src/handlers/no_such_field.rs2
-rw-r--r--crates/ide/src/inlay_hints.rs67
-rw-r--r--crates/ide/src/inlay_hints/adjustment.rs1
-rw-r--r--crates/ide/src/inlay_hints/bind_pat.rs191
-rw-r--r--crates/ide/src/inlay_hints/binding_mode.rs8
-rw-r--r--crates/ide/src/inlay_hints/chaining.rs26
-rw-r--r--crates/ide/src/inlay_hints/closing_brace.rs1
-rw-r--r--crates/ide/src/inlay_hints/closure_ret.rs30
-rw-r--r--crates/ide/src/inlay_hints/discriminant.rs1
-rw-r--r--crates/ide/src/inlay_hints/fn_lifetime_fn.rs3
-rw-r--r--crates/ide/src/inlay_hints/implicit_static.rs1
-rw-r--r--crates/ide/src/inlay_hints/param_name.rs1
-rw-r--r--crates/rust-analyzer/src/to_proto.rs2
27 files changed, 374 insertions, 49 deletions
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index f892a81519..0eef25102e 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -150,6 +150,7 @@ pub trait HirDisplay {
&'a self,
db: &'a dyn HirDatabase,
module_id: ModuleId,
+ allow_opaque: bool,
) -> Result<String, DisplaySourceCodeError> {
let mut result = String::new();
match self.hir_fmt(&mut HirFormatter {
@@ -160,7 +161,7 @@ pub trait HirDisplay {
max_size: None,
omit_verbose_types: false,
closure_style: ClosureStyle::ImplFn,
- display_target: DisplayTarget::SourceCode { module_id },
+ display_target: DisplayTarget::SourceCode { module_id, allow_opaque },
}) {
Ok(()) => {}
Err(HirDisplayError::FmtError) => panic!("Writing to String can't fail!"),
@@ -249,18 +250,26 @@ pub enum DisplayTarget {
Diagnostics,
/// Display types for inserting them in source files.
/// The generated code should compile, so paths need to be qualified.
- SourceCode { module_id: ModuleId },
+ SourceCode { module_id: ModuleId, allow_opaque: bool },
/// Only for test purpose to keep real types
Test,
}
impl DisplayTarget {
- fn is_source_code(&self) -> bool {
+ fn is_source_code(self) -> bool {
matches!(self, Self::SourceCode { .. })
}
- fn is_test(&self) -> bool {
+
+ fn is_test(self) -> bool {
matches!(self, Self::Test)
}
+
+ fn allows_opaque(self) -> bool {
+ match self {
+ Self::SourceCode { allow_opaque, .. } => allow_opaque,
+ _ => true,
+ }
+ }
}
#[derive(Debug)]
@@ -268,6 +277,7 @@ pub enum DisplaySourceCodeError {
PathNotFound,
UnknownType,
Generator,
+ OpaqueType,
}
pub enum HirDisplayError {
@@ -768,7 +778,7 @@ impl HirDisplay for Ty {
};
write!(f, "{name}")?;
}
- DisplayTarget::SourceCode { module_id } => {
+ DisplayTarget::SourceCode { module_id, allow_opaque: _ } => {
if let Some(path) = find_path::find_path(
db.upcast(),
ItemInNs::Types((*def_id).into()),
@@ -906,6 +916,11 @@ impl HirDisplay for Ty {
f.end_location_link();
}
TyKind::OpaqueType(opaque_ty_id, parameters) => {
+ if !f.display_target.allows_opaque() {
+ return Err(HirDisplayError::DisplaySourceCodeError(
+ DisplaySourceCodeError::OpaqueType,
+ ));
+ }
let impl_trait_id = db.lookup_intern_impl_trait_id((*opaque_ty_id).into());
match impl_trait_id {
ImplTraitId::ReturnTypeImplTrait(func, idx) => {
@@ -953,8 +968,14 @@ impl HirDisplay for Ty {
}
}
TyKind::Closure(id, substs) => {
- if f.display_target.is_source_code() && f.closure_style != ClosureStyle::ImplFn {
- never!("Only `impl Fn` is valid for displaying closures in source code");
+ if f.display_target.is_source_code() {
+ if !f.display_target.allows_opaque() {
+ return Err(HirDisplayError::DisplaySourceCodeError(
+ DisplaySourceCodeError::OpaqueType,
+ ));
+ } else if f.closure_style != ClosureStyle::ImplFn {
+ never!("Only `impl Fn` is valid for displaying closures in source code");
+ }
}
match f.closure_style {
ClosureStyle::Hide => return write!(f, "{TYPE_HINT_TRUNCATION}"),
@@ -1053,6 +1074,11 @@ impl HirDisplay for Ty {
}
TyKind::Alias(AliasTy::Projection(p_ty)) => p_ty.hir_fmt(f)?,
TyKind::Alias(AliasTy::Opaque(opaque_ty)) => {
+ if !f.display_target.allows_opaque() {
+ return Err(HirDisplayError::DisplaySourceCodeError(
+ DisplaySourceCodeError::OpaqueType,
+ ));
+ }
let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty.opaque_ty_id.into());
match impl_trait_id {
ImplTraitId::ReturnTypeImplTrait(func, idx) => {
diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs
index 1e46bb1d04..245617ab82 100644
--- a/crates/hir-ty/src/tests.rs
+++ b/crates/hir-ty/src/tests.rs
@@ -159,7 +159,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
let range = node.as_ref().original_file_range(&db);
if let Some(expected) = types.remove(&range) {
let actual = if display_source {
- ty.display_source_code(&db, def.module(&db)).unwrap()
+ ty.display_source_code(&db, def.module(&db), true).unwrap()
} else {
ty.display_test(&db).to_string()
};
@@ -175,7 +175,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
let range = node.as_ref().original_file_range(&db);
if let Some(expected) = types.remove(&range) {
let actual = if display_source {
- ty.display_source_code(&db, def.module(&db)).unwrap()
+ ty.display_source_code(&db, def.module(&db), true).unwrap()
} else {
ty.display_test(&db).to_string()
};
diff --git a/crates/ide-assists/src/handlers/add_explicit_type.rs b/crates/ide-assists/src/handlers/add_explicit_type.rs
index 785ae3d09c..8bc285614e 100644
--- a/crates/ide-assists/src/handlers/add_explicit_type.rs
+++ b/crates/ide-assists/src/handlers/add_explicit_type.rs
@@ -69,7 +69,7 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
return None;
}
- let inferred_type = ty.display_source_code(ctx.db(), module.into()).ok()?;
+ let inferred_type = ty.display_source_code(ctx.db(), module.into(), false).ok()?;
acc.add(
AssistId("add_explicit_type", AssistKind::RefactorRewrite),
format!("Insert explicit type `{inferred_type}`"),
diff --git a/crates/ide-assists/src/handlers/add_return_type.rs b/crates/ide-assists/src/handlers/add_return_type.rs
index 879c478acf..9e1022d809 100644
--- a/crates/ide-assists/src/handlers/add_return_type.rs
+++ b/crates/ide-assists/src/handlers/add_return_type.rs
@@ -22,7 +22,7 @@ pub(crate) fn add_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
if ty.is_unit() {
return None;
}
- let ty = ty.display_source_code(ctx.db(), module.into()).ok()?;
+ let ty = ty.display_source_code(ctx.db(), module.into(), true).ok()?;
acc.add(
AssistId("add_return_type", AssistKind::RefactorRewrite),
diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs
index bfa2890676..728018506d 100644
--- a/crates/ide-assists/src/handlers/extract_function.rs
+++ b/crates/ide-assists/src/handlers/extract_function.rs
@@ -1884,7 +1884,7 @@ fn with_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr
}
fn format_type(ty: &hir::Type, ctx: &AssistContext<'_>, module: hir::Module) -> String {
- ty.display_source_code(ctx.db(), module.into()).ok().unwrap_or_else(|| "_".to_string())
+ ty.display_source_code(ctx.db(), module.into(), true).ok().unwrap_or_else(|| "_".to_string())
}
fn make_ty(ty: &hir::Type, ctx: &AssistContext<'_>, module: hir::Module) -> ast::Type {
diff --git a/crates/ide-assists/src/handlers/generate_constant.rs b/crates/ide-assists/src/handlers/generate_constant.rs
index 57bb679729..eccd7675fb 100644
--- a/crates/ide-assists/src/handlers/generate_constant.rs
+++ b/crates/ide-assists/src/handlers/generate_constant.rs
@@ -46,7 +46,8 @@ pub(crate) fn generate_constant(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
let ty = ctx.sema.type_of_expr(&expr)?;
let scope = ctx.sema.scope(statement.syntax())?;
let constant_module = scope.module();
- let type_name = ty.original().display_source_code(ctx.db(), constant_module.into()).ok()?;
+ let type_name =
+ ty.original().display_source_code(ctx.db(), constant_module.into(), false).ok()?;
let target = statement.syntax().parent()?.text_range();
let path = constant_token.syntax().ancestors().find_map(ast::Path::cast)?;
diff --git a/crates/ide-assists/src/handlers/generate_enum_variant.rs b/crates/ide-assists/src/handlers/generate_enum_variant.rs
index cd037f7492..184f523e01 100644
--- a/crates/ide-assists/src/handlers/generate_enum_variant.rs
+++ b/crates/ide-assists/src/handlers/generate_enum_variant.rs
@@ -192,7 +192,7 @@ fn expr_ty(
scope: &hir::SemanticsScope<'_>,
) -> Option<ast::Type> {
let ty = ctx.sema.type_of_expr(&arg).map(|it| it.adjusted())?;
- let text = ty.display_source_code(ctx.db(), scope.module().into()).ok()?;
+ let text = ty.display_source_code(ctx.db(), scope.module().into(), false).ok()?;
Some(make::ty(&text))
}
diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs
index 2372fe28e1..a5556878be 100644
--- a/crates/ide-assists/src/handlers/generate_function.rs
+++ b/crates/ide-assists/src/handlers/generate_function.rs
@@ -438,7 +438,7 @@ fn make_return_type(
Some(ty) if ty.is_unit() => (None, false),
Some(ty) => {
necessary_generic_params.extend(ty.generic_params(ctx.db()));
- let rendered = ty.display_source_code(ctx.db(), target_module.into());
+ let rendered = ty.display_source_code(ctx.db(), target_module.into(), true);
match rendered {
Ok(rendered) => (Some(make::ty(&rendered)), false),
Err(_) => (Some(make::ty_placeholder()), true),
@@ -992,9 +992,9 @@ fn fn_arg_type(
let famous_defs = &FamousDefs(&ctx.sema, ctx.sema.scope(fn_arg.syntax())?.krate());
convert_reference_type(ty.strip_references(), ctx.db(), famous_defs)
.map(|conversion| conversion.convert_type(ctx.db()))
- .or_else(|| ty.display_source_code(ctx.db(), target_module.into()).ok())
+ .or_else(|| ty.display_source_code(ctx.db(), target_module.into(), true).ok())
} else {
- ty.display_source_code(ctx.db(), target_module.into()).ok()
+ ty.display_source_code(ctx.db(), target_module.into(), true).ok()
}
}
diff --git a/crates/ide-assists/src/handlers/promote_local_to_const.rs b/crates/ide-assists/src/handlers/promote_local_to_const.rs
index cbbea6c1e6..23153b4c56 100644
--- a/crates/ide-assists/src/handlers/promote_local_to_const.rs
+++ b/crates/ide-assists/src/handlers/promote_local_to_const.rs
@@ -57,11 +57,13 @@ pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_>)
let local = ctx.sema.to_def(&pat)?;
let ty = ctx.sema.type_of_pat(&pat.into())?.original;
- if ty.contains_unknown() || ty.is_closure() {
- cov_mark::hit!(promote_lcoal_not_applicable_if_ty_not_inferred);
- return None;
- }
- let ty = ty.display_source_code(ctx.db(), module.into()).ok()?;
+ let ty = match ty.display_source_code(ctx.db(), module.into(), false) {
+ Ok(ty) => ty,
+ Err(_) => {
+ cov_mark::hit!(promote_local_not_applicable_if_ty_not_inferred);
+ return None;
+ }
+ };
let initializer = let_stmt.initializer()?;
if !is_body_const(&ctx.sema, &initializer) {
@@ -187,7 +189,7 @@ fn foo() {
#[test]
fn not_applicable_unknown_ty() {
- cov_mark::check!(promote_lcoal_not_applicable_if_ty_not_inferred);
+ cov_mark::check!(promote_local_not_applicable_if_ty_not_inferred);
check_assist_not_applicable(
promote_local_to_const,
r"
diff --git a/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs b/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs
index 6626ce0795..43a97d7d3a 100644
--- a/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs
+++ b/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs
@@ -55,7 +55,7 @@ pub(crate) fn replace_turbofish_with_explicit_type(
let returned_type = match ctx.sema.type_of_expr(&initializer) {
Some(returned_type) if !returned_type.original.contains_unknown() => {
let module = ctx.sema.scope(let_stmt.syntax())?.module();
- returned_type.original.display_source_code(ctx.db(), module.into()).ok()?
+ returned_type.original.display_source_code(ctx.db(), module.into(), false).ok()?
}
_ => {
cov_mark::hit!(fallback_to_turbofish_type_if_type_info_not_available);
diff --git a/crates/ide-completion/src/completions/fn_param.rs b/crates/ide-completion/src/completions/fn_param.rs
index d8b8a190eb..734e1bed8d 100644
--- a/crates/ide-completion/src/completions/fn_param.rs
+++ b/crates/ide-completion/src/completions/fn_param.rs
@@ -127,7 +127,7 @@ fn params_from_stmt_list_scope(
let module = scope.module().into();
scope.process_all_names(&mut |name, def| {
if let hir::ScopeDef::Local(local) = def {
- if let Ok(ty) = local.ty(ctx.db).display_source_code(ctx.db, module) {
+ if let Ok(ty) = local.ty(ctx.db).display_source_code(ctx.db, module, true) {
cb(name, ty);
}
}
diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs
index 2ad9520cd6..e470547563 100644
--- a/crates/ide-completion/src/completions/type.rs
+++ b/crates/ide-completion/src/completions/type.rs
@@ -242,7 +242,7 @@ pub(crate) fn complete_ascribed_type(
}
}?
.adjusted();
- let ty_string = x.display_source_code(ctx.db, ctx.module.into()).ok()?;
+ let ty_string = x.display_source_code(ctx.db, ctx.module.into(), true).ok()?;
acc.add(render_type_inference(ty_string, ctx));
None
}
diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs
index 6402a84a68..0ee627a44c 100644
--- a/crates/ide-db/src/path_transform.rs
+++ b/crates/ide-db/src/path_transform.rs
@@ -116,7 +116,9 @@ impl<'a> PathTransform<'a> {
Some((
k,
ast::make::ty(
- &default.display_source_code(db, source_module.into()).ok()?,
+ &default
+ .display_source_code(db, source_module.into(), false)
+ .ok()?,
),
))
}
diff --git a/crates/ide-diagnostics/src/handlers/missing_fields.rs b/crates/ide-diagnostics/src/handlers/missing_fields.rs
index 5c4327ff93..a33a2cd85e 100644
--- a/crates/ide-diagnostics/src/handlers/missing_fields.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_fields.rs
@@ -176,7 +176,9 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
fn make_ty(ty: &hir::Type, db: &dyn HirDatabase, module: hir::Module) -> ast::Type {
let ty_str = match ty.as_adt() {
Some(adt) => adt.name(db).to_string(),
- None => ty.display_source_code(db, module.into()).ok().unwrap_or_else(|| "_".to_string()),
+ None => {
+ ty.display_source_code(db, module.into(), false).ok().unwrap_or_else(|| "_".to_string())
+ }
};
make::ty(&ty_str)
diff --git a/crates/ide-diagnostics/src/handlers/no_such_field.rs b/crates/ide-diagnostics/src/handlers/no_such_field.rs
index 24c521ed1a..625c95ce20 100644
--- a/crates/ide-diagnostics/src/handlers/no_such_field.rs
+++ b/crates/ide-diagnostics/src/handlers/no_such_field.rs
@@ -69,7 +69,7 @@ fn missing_record_expr_field_fixes(
let new_field = make::record_field(
None,
make::name(record_expr_field.field_name()?.ident_token()?.text()),
- make::ty(&new_field_type.display_source_code(sema.db, module.into()).ok()?),
+ make::ty(&new_field_type.display_source_code(sema.db, module.into(), true).ok()?),
);
let last_field = record_fields.fields().last()?;
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index e6360bc6ec..7a8edfea83 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -14,8 +14,9 @@ use smallvec::{smallvec, SmallVec};
use stdx::never;
use syntax::{
ast::{self, AstNode},
- match_ast, NodeOrToken, SyntaxNode, TextRange,
+ match_ast, NodeOrToken, SyntaxNode, TextRange, TextSize,
};
+use text_edit::TextEdit;
use crate::{navigation_target::TryToNav, FileId};
@@ -113,14 +114,26 @@ pub struct InlayHint {
pub kind: InlayKind,
/// The actual label to show in the inlay hint.
pub label: InlayHintLabel,
+ /// Text edit to apply when "accepting" this inlay hint.
+ pub text_edit: Option<TextEdit>,
}
impl InlayHint {
fn closing_paren(range: TextRange) -> InlayHint {
- InlayHint { range, kind: InlayKind::ClosingParenthesis, label: InlayHintLabel::from(")") }
+ InlayHint {
+ range,
+ kind: InlayKind::ClosingParenthesis,
+ label: InlayHintLabel::from(")"),
+ text_edit: None,
+ }
}
fn opening_paren(range: TextRange) -> InlayHint {
- InlayHint { range, kind: InlayKind::OpeningParenthesis, label: InlayHintLabel::from("(") }
+ InlayHint {
+ range,
+ kind: InlayKind::OpeningParenthesis,
+ label: InlayHintLabel::from("("),
+ text_edit: None,
+ }
}
}
@@ -346,6 +359,23 @@ fn label_of_ty(
Some(r)
}
+fn ty_to_text_edit(
+ sema: &Semantics<'_, RootDatabase>,
+ node_for_hint: &SyntaxNode,
+ ty: &hir::Type,
+ offset_to_insert: TextSize,
+ prefix: String,
+) -> Option<TextEdit> {
+ let scope = sema.scope(node_for_hint)?;
+ // FIXME: Limit the length and bail out on excess somehow?
+ let rendered = ty.display_source_code(scope.db, scope.module().into(), false).ok()?;
+
+ let mut builder = TextEdit::builder();
+ builder.insert(offset_to_insert, prefix);
+ builder.insert(offset_to_insert, rendered);
+ Some(builder.finish())
+}
+
// Feature: Inlay Hints
//
// rust-analyzer shows additional information inline with the source code.
@@ -553,6 +583,37 @@ mod tests {
expect.assert_debug_eq(&inlay_hints)
}
+ /// Computes inlay hints for the fixture, applies all the provided text edits and then runs
+ /// expect test.
+ #[track_caller]
+ pub(super) fn check_edit(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 edits = inlay_hints
+ .into_iter()
+ .filter_map(|hint| hint.text_edit)
+ .reduce(|mut acc, next| {
+ acc.union(next).expect("merging text edits failed");
+ acc
+ })
+ .expect("no edit returned");
+
+ let mut actual = analysis.file_text(file_id).unwrap().to_string();
+ edits.apply(&mut actual);
+ expect.assert_eq(&actual);
+ }
+
+ #[track_caller]
+ pub(super) fn check_no_edit(config: InlayHintsConfig, ra_fixture: &str) {
+ let (analysis, file_id) = fixture::file(ra_fixture);
+ let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
+
+ let edits: Vec<_> = inlay_hints.into_iter().filter_map(|hint| hint.text_edit).collect();
+
+ assert!(edits.is_empty(), "unexpected edits: {edits:?}");
+ }
+
#[test]
fn hints_disabled() {
check_with_config(
diff --git a/crates/ide/src/inlay_hints/adjustment.rs b/crates/ide/src/inlay_hints/adjustment.rs
index 0b14609195..fea4b89b89 100644
--- a/crates/ide/src/inlay_hints/adjustment.rs
+++ b/crates/ide/src/inlay_hints/adjustment.rs
@@ -135,6 +135,7 @@ pub(super) fn hints(
))),
None,
),
+ text_edit: None,
});
}
if !postfix && needs_inner_parens {
diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs
index 5f571d0448..a131427f5f 100644
--- a/crates/ide/src/inlay_hints/bind_pat.rs
+++ b/crates/ide/src/inlay_hints/bind_pat.rs
@@ -12,9 +12,10 @@ use syntax::{
match_ast,
};
-use crate::{inlay_hints::closure_has_block_body, InlayHint, InlayHintsConfig, InlayKind};
-
-use super::label_of_ty;
+use crate::{
+ inlay_hints::{closure_has_block_body, label_of_ty, ty_to_text_edit},
+ InlayHint, InlayHintsConfig, InlayKind,
+};
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
@@ -35,7 +36,7 @@ pub(super) fn hints(
return None;
}
- let label = label_of_ty(famous_defs, config, ty)?;
+ let label = label_of_ty(famous_defs, config, ty.clone())?;
if config.hide_named_constructor_hints
&& is_named_constructor(sema, pat, &label.to_string()).is_some()
@@ -43,6 +44,23 @@ pub(super) fn hints(
return None;
}
+ let type_annotation_is_valid = desc_pat
+ .syntax()
+ .parent()
+ .map(|it| ast::LetStmt::can_cast(it.kind()) || ast::Param::can_cast(it.kind()))
+ .unwrap_or(false);
+ let text_edit = if type_annotation_is_valid {
+ ty_to_text_edit(
+ sema,
+ desc_pat.syntax(),
+ &ty,
+ pat.syntax().text_range().end(),
+ String::from(": "),
+ )
+ } else {
+ None
+ };
+
acc.push(InlayHint {
range: match pat.name() {
Some(name) => name.syntax().text_range(),
@@ -50,6 +68,7 @@ pub(super) fn hints(
},
kind: InlayKind::Type,
label,
+ text_edit,
});
Some(())
@@ -176,14 +195,16 @@ fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &hir
mod tests {
// This module also contains tests for super::closure_ret
+ use expect_test::expect;
use hir::ClosureStyle;
use syntax::{TextRange, TextSize};
use test_utils::extract_annotations;
- use crate::{fixture, inlay_hints::InlayHintsConfig};
+ use crate::{fixture, inlay_hints::InlayHintsConfig, ClosureReturnTypeHints};
- use crate::inlay_hints::tests::{check, check_with_config, DISABLED_CONFIG, TEST_CONFIG};
- use crate::ClosureReturnTypeHints;
+ use crate::inlay_hints::tests::{
+ check, check_edit, check_no_edit, check_with_config, DISABLED_CONFIG, TEST_CONFIG,
+ };
#[track_caller]
fn check_types(ra_fixture: &str) {
@@ -1012,4 +1033,160 @@ fn main() {
}"#,
);
}
+
+ #[test]
+ fn edit_for_let_stmt() {
+ check_edit(
+ TEST_CONFIG,
+ r#"
+struct S<T>(T);
+fn test<F>(v: S<(S<i32>, S<()>)>, f: F) {
+ let a = v;
+ let S((b, c)) = v;
+ let a @ S((b, c)) = v;
+ let a = f;
+}
+"#,
+ expect![[r#"
+ struct S<T>(T);
+ fn test<F>(v: S<(S<i32>, S<()>)>, f: F) {
+ let a: S<(S<i32>, S<()>)> = v;
+ let S((b, c)) = v;
+ let a @ S((b, c)): S<(S<i32>, S<()>)> = v;
+ let a: F = f;
+ }
+ "#]],
+ );
+ }
+
+ #[test]
+ fn edit_for_closure_param() {
+ check_edit(
+ TEST_CONFIG,
+ r#"
+fn test<T>(t: T) {
+ let f = |a, b, c| {};
+ let result = f(42, "", t);
+}
+"#,
+ expect![[r#"
+ fn test<T>(t: T) {
+ let f = |a: i32, b: &str, c: T| {};
+ let result: () = f(42, "", t);
+ }
+ "#]],
+ );
+ }
+
+ #[test]
+ fn edit_for_closure_ret() {
+ check_edit(
+ TEST_CONFIG,
+ r#"
+struct S<T>(T);
+fn test() {
+ let f = || { 3 };
+ let f = |a: S<usize>| { S(a) };
+}
+"#,
+ expect![[r#"
+ struct S<T>(T);
+ fn test() {
+ let f = || -> i32 { 3 };
+ let f = |a: S<usize>| -> S<S<usize>> { S(a) };
+ }
+ "#]],
+ );
+ }
+
+ #[test]
+ fn edit_prefixes_paths() {
+ check_edit(
+ TEST_CONFIG,
+ r#"
+pub struct S<T>(T);
+mod middle {
+ pub struct S<T, U>(T, U);
+ pub fn make() -> S<inner::S<i64>, super::S<usize>> { loop {} }
+
+ mod inner {
+ pub struct S<T>(T);
+ }
+
+ fn test() {
+ let a = make();
+ }
+}
+"#,
+ expect![[r#"
+ pub struct S<T>(T);
+ mod middle {
+ pub struct S<T, U>(T, U);
+ pub fn make() -> S<inner::S<i64>, super::S<usize>> { loop {} }
+
+ mod inner {
+ pub struct S<T>(T);
+ }
+
+ fn test() {
+ let a: S<inner::S<i64>, crate::S<usize>> = make();
+ }
+ }
+ "#]],
+ );
+ }
+
+ #[test]
+ fn no_edit_for_top_pat_where_type_annotation_is_invalid() {
+ check_no_edit(
+ TEST_CONFIG,
+ r#"
+fn test() {
+ if let a = 42 {}
+ while let a = 42 {}
+ match 42 {
+ a => (),
+ }
+}
+"#,
+ )
+ }
+
+ #[test]
+ fn no_edit_for_opaque_type() {
+ check_no_edit(
+ TEST_CONFIG,
+ r#"
+trait Trait {}
+struct S<T>(T);
+fn foo() -> impl Trait {}
+fn bar() -> S<impl Trait> {}
+fn test() {
+ let a = foo();
+ let a = bar();
+ let f = || { foo() };
+ let f = || { bar() };
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn no_edit_for_closure_return_without_body_block() {
+ // We can lift this limitation; see FIXME in closure_ret module.
+ let config = InlayHintsConfig {
+ closure_return_type_hints: ClosureReturnTypeHints::Always,
+ ..TEST_CONFIG
+ };
+ check_no_edit(
+ config,
+ r#"
+struct S<T>(T);
+fn test() {
+ let f = || 3;
+ let f = |a: S<usize>| S(a);
+}
+"#,
+ );
+ }
}
diff --git a/crates/ide/src/inlay_hints/binding_mode.rs b/crates/ide/src/inlay_hints/binding_mode.rs
index 5d9729263c..3d7f969aaa 100644
--- a/crates/ide/src/inlay_hints/binding_mode.rs
+++ b/crates/ide/src/inlay_hints/binding_mode.rs
@@ -49,7 +49,12 @@ pub(super) fn hints(
(true, false) => "&",
_ => return,
};
- acc.push(InlayHint { range, kind: InlayKind::BindingMode, label: r.to_string().into() });
+ acc.push(InlayHint {
+ range,
+ kind: InlayKind::BindingMode,
+ label: r.to_string().into(),
+ text_edit: None,
+ });
});
match pat {
ast::Pat::IdentPat(pat) if pat.ref_token().is_none() && pat.mut_token().is_none() => {
@@ -63,6 +68,7 @@ pub(super) fn hints(
range: pat.syntax().text_range(),
kind: InlayKind::BindingMode,
label: bm.to_string().into(),
+ text_edit: None,
});
}
ast::Pat::OrPat(pat) if !pattern_adjustments.is_empty() && outer_paren_pat.is_none() => {
diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs
index 11e6dc05fa..6db9b8b544 100644
--- a/crates/ide/src/inlay_hints/chaining.rs
+++ b/crates/ide/src/inlay_hints/chaining.rs
@@ -61,6 +61,7 @@ pub(super) fn hints(
range: expr.syntax().text_range(),
kind: InlayKind::Chaining,
label: label_of_ty(famous_defs, config, ty)?,
+ text_edit: None,
});
}
}
@@ -120,6 +121,7 @@ fn main() {
},
"",
],
+ text_edit: None,
},
InlayHint {
range: 147..154,
@@ -140,6 +142,7 @@ fn main() {
},
"",
],
+ text_edit: None,
},
]
"#]],
@@ -205,6 +208,7 @@ fn main() {
},
"",
],
+ text_edit: None,
},
InlayHint {
range: 143..179,
@@ -225,6 +229,7 @@ fn main() {
},
"",
],
+ text_edit: None,
},
]
"#]],
@@ -274,6 +279,7 @@ fn main() {
},
"",
],
+ text_edit: None,
},
InlayHint {
range: 143..179,
@@ -294,6 +300,7 @@ fn main() {
},
"",
],
+ text_edit: None,
},
]
"#]],
@@ -357,6 +364,7 @@ fn main() {
},
"<i32, bool>>",
],
+ text_edit: None,
},
InlayHint {
range: 246..265,
@@ -390,6 +398,7 @@ fn main() {
},
"<i32, bool>>",
],
+ text_edit: None,
},
]
"#]],
@@ -455,6 +464,7 @@ fn main() {
},
" = ()>",
],
+ text_edit: None,
},
InlayHint {
range: 174..224,
@@ -488,6 +498,7 @@ fn main() {
},
" = ()>",
],
+ text_edit: None,
},
InlayHint {
range: 174..206,
@@ -521,6 +532,7 @@ fn main() {
},
" = ()>",
],
+ text_edit: None,
},
InlayHint {
range: 174..189,
@@ -541,6 +553,7 @@ fn main() {
},
"",
],
+ text_edit: None,
},
]
"#]],
@@ -590,6 +603,16 @@ fn main() {
},
"",
],
+ text_edit: Some(
+ TextEdit {
+ indels: [
+ Indel {
+ insert: ": Struct",
+ delete: 130..130,
+ },
+ ],
+ },
+ ),
},
InlayHint {
range: 145..185,
@@ -610,6 +633,7 @@ fn main() {
},
"",
],
+ text_edit: None,
},
InlayHint {
range: 145..168,
@@ -630,6 +654,7 @@ fn main() {
},
"",
],
+ text_edit: None,
},
InlayHint {
range: 222..228,
@@ -648,6 +673,7 @@ fn main() {
tooltip: "",
},
],
+ text_edit: None,
},
]
"#]],
diff --git a/crates/ide/src/inlay_hints/closing_brace.rs b/crates/ide/src/inlay_hints/closing_brace.rs
index 14c11be54e..10b5acd064 100644
--- a/crates/ide/src/inlay_hints/closing_brace.rs
+++ b/crates/ide/src/inlay_hints/closing_brace.rs
@@ -112,6 +112,7 @@ pub(super) fn hints(
range: closing_token.text_range(),
kind: InlayKind::ClosingBrace,
label: InlayHintLabel::simple(label, None, linked_location),
+ text_edit: None,
});
None
diff --git a/crates/ide/src/inlay_hints/closure_ret.rs b/crates/ide/src/inlay_hints/closure_ret.rs
index f03a18b8e9..6214e9c8e7 100644
--- a/crates/ide/src/inlay_hints/closure_ret.rs
+++ b/crates/ide/src/inlay_hints/closure_ret.rs
@@ -1,14 +1,14 @@
//! Implementation of "closure return type" inlay hints.
+//!
+//! Tests live in [`bind_pat`][super::bind_pat] module.
use ide_db::{base_db::FileId, famous_defs::FamousDefs};
use syntax::ast::{self, AstNode};
use crate::{
- inlay_hints::closure_has_block_body, ClosureReturnTypeHints, InlayHint, InlayHintsConfig,
- InlayKind,
+ inlay_hints::{closure_has_block_body, label_of_ty, ty_to_text_edit},
+ ClosureReturnTypeHints, InlayHint, InlayHintsConfig, InlayKind,
};
-use super::label_of_ty;
-
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
@@ -24,25 +24,39 @@ pub(super) fn hints(
return None;
}
- if !closure_has_block_body(&closure)
- && config.closure_return_type_hints == ClosureReturnTypeHints::WithBlock
- {
+ let has_block_body = closure_has_block_body(&closure);
+ if !has_block_body && config.closure_return_type_hints == ClosureReturnTypeHints::WithBlock {
return None;
}
let param_list = closure.param_list()?;
let closure = sema.descend_node_into_attributes(closure).pop()?;
- let ty = sema.type_of_expr(&ast::Expr::ClosureExpr(closure))?.adjusted();
+ let ty = sema.type_of_expr(&ast::Expr::ClosureExpr(closure.clone()))?.adjusted();
let callable = ty.as_callable(sema.db)?;
let ty = callable.return_type();
if ty.is_unit() {
return None;
}
+
+ // FIXME?: We could provide text edit to insert braces for closures with non-block body.
+ let text_edit = if has_block_body {
+ ty_to_text_edit(
+ sema,
+ closure.syntax(),
+ &ty,
+ param_list.syntax().text_range().end(),
+ String::from(" -> "),
+ )
+ } else {
+ None
+ };
+
acc.push(InlayHint {
range: param_list.syntax().text_range(),
kind: InlayKind::ClosureReturnType,
label: label_of_ty(famous_defs, config, ty)?,
+ text_edit,
});
Some(())
}
diff --git a/crates/ide/src/inlay_hints/discriminant.rs b/crates/ide/src/inlay_hints/discriminant.rs
index 67eaa553ad..f9047efaf1 100644
--- a/crates/ide/src/inlay_hints/discriminant.rs
+++ b/crates/ide/src/inlay_hints/discriminant.rs
@@ -75,6 +75,7 @@ fn variant_hints(
})),
None,
),
+ text_edit: None,
});
Some(())
diff --git a/crates/ide/src/inlay_hints/fn_lifetime_fn.rs b/crates/ide/src/inlay_hints/fn_lifetime_fn.rs
index b7182085b3..34eb5eb94c 100644
--- a/crates/ide/src/inlay_hints/fn_lifetime_fn.rs
+++ b/crates/ide/src/inlay_hints/fn_lifetime_fn.rs
@@ -25,6 +25,7 @@ pub(super) fn hints(
range: t.text_range(),
kind: InlayKind::Lifetime,
label: label.into(),
+ text_edit: None,
};
let param_list = func.param_list()?;
@@ -189,12 +190,14 @@ pub(super) fn hints(
if is_empty { "" } else { ", " }
)
.into(),
+ text_edit: None,
});
}
(None, allocated_lifetimes) => acc.push(InlayHint {
range: func.name()?.syntax().text_range(),
kind: InlayKind::GenericParamList,
label: format!("<{}>", allocated_lifetimes.iter().format(", "),).into(),
+ text_edit: None,
}),
}
Some(())
diff --git a/crates/ide/src/inlay_hints/implicit_static.rs b/crates/ide/src/inlay_hints/implicit_static.rs
index 1122ee2e39..ba875649f7 100644
--- a/crates/ide/src/inlay_hints/implicit_static.rs
+++ b/crates/ide/src/inlay_hints/implicit_static.rs
@@ -34,6 +34,7 @@ pub(super) fn hints(
range: t.text_range(),
kind: InlayKind::Lifetime,
label: "'static".to_owned().into(),
+ text_edit: None,
});
}
}
diff --git a/crates/ide/src/inlay_hints/param_name.rs b/crates/ide/src/inlay_hints/param_name.rs
index 9cdae63241..9729a43c22 100644
--- a/crates/ide/src/inlay_hints/param_name.rs
+++ b/crates/ide/src/inlay_hints/param_name.rs
@@ -57,6 +57,7 @@ pub(super) fn hints(
range,
kind: InlayKind::Parameter,
label: InlayHintLabel::simple(param_name, None, linked_location),
+ text_edit: None,
}
});
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 2b9dfeccef..cc72c2e10b 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -510,7 +510,7 @@ pub(crate) fn inlay_hint(
| InlayKind::AdjustmentPostfix
| InlayKind::ClosingBrace => None,
},
- text_edits: None,
+ text_edits: inlay_hint.text_edit.map(|it| text_edit_vec(line_index, it)),
data: None,
tooltip,
label,