diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index b537150608..414627fbab 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -617,6 +617,16 @@ impl Completions { } self.add_opt(render_struct_pat(RenderContext::new(ctx), pattern_ctx, strukt, local_name)); } + + pub(crate) fn suggest_name(&mut self, ctx: &CompletionContext<'_>, name: &str) { + let item = CompletionItem::new( + CompletionItemKind::Binding, + ctx.source_range(), + SmolStr::from(name), + ctx.edition, + ); + item.add_to(self, ctx.db); + } } /// Calls the callback for each variant of the provided enum with the path to the variant. diff --git a/crates/ide-completion/src/completions/pattern.rs b/crates/ide-completion/src/completions/pattern.rs index 60cfb7e5a8..2a06fc4017 100644 --- a/crates/ide-completion/src/completions/pattern.rs +++ b/crates/ide-completion/src/completions/pattern.rs @@ -1,6 +1,7 @@ //! Completes constants and paths in unqualified patterns. use hir::{db::DefDatabase, AssocItem, ScopeDef}; +use ide_db::syntax_helpers::suggest_name; use syntax::ast::Pat; use crate::{ @@ -45,6 +46,18 @@ pub(crate) fn complete_pattern( return; } + // Suggest name only in let-stmt and fn param + if pattern_ctx.should_suggest_name { + if let Some(suggested) = ctx + .expected_type + .as_ref() + .map(|ty| ty.strip_references()) + .and_then(|ty| suggest_name::for_type(&ty, ctx.db, ctx.edition)) + { + acc.suggest_name(ctx, &suggested); + } + } + let refutable = pattern_ctx.refutability == PatternRefutability::Refutable; let single_variant_enum = |enum_: hir::Enum| ctx.db.enum_data(enum_.into()).variants.len() == 1; diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index bcd9df9419..d457ba32bf 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -264,6 +264,7 @@ pub(crate) struct PatternContext { pub(crate) refutability: PatternRefutability, pub(crate) param_ctx: Option, pub(crate) has_type_ascription: bool, + pub(crate) should_suggest_name: bool, pub(crate) parent_pat: Option, pub(crate) ref_token: Option, pub(crate) mut_token: Option, diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 292c419498..1f9e3edf62 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -1430,10 +1430,23 @@ fn pattern_context_for( _ => (None, None), }; + // Only suggest name in let-stmt or fn param + let should_suggest_name = matches!( + &pat, + ast::Pat::IdentPat(it) + if it.syntax() + .parent() + .map_or(false, |node| { + let kind = node.kind(); + ast::LetStmt::can_cast(kind) || ast::Param::can_cast(kind) + }) + ); + PatternContext { refutability, param_ctx, has_type_ascription, + should_suggest_name, parent_pat: pat.syntax().parent().and_then(ast::Pat::cast), mut_token, ref_token, diff --git a/crates/ide-db/src/syntax_helpers/suggest_name.rs b/crates/ide-db/src/syntax_helpers/suggest_name.rs index 14128e7443..6ee526a67e 100644 --- a/crates/ide-db/src/syntax_helpers/suggest_name.rs +++ b/crates/ide-db/src/syntax_helpers/suggest_name.rs @@ -60,6 +60,21 @@ const USELESS_METHODS: &[&str] = &[ "into_future", ]; +/// Suggest a name for given type. +/// +/// The function will strip references first, and suggest name from the inner type. +/// +/// - If `ty` is an ADT, it will suggest the name of the ADT. +/// + If `ty` is wrapped in `Box`, `Option` or `Result`, it will suggest the name from the inner type. +/// - If `ty` is a trait, it will suggest the name of the trait. +/// - If `ty` is an `impl Trait`, it will suggest the name of the first trait. +/// +/// If the suggested name conflicts with reserved keywords, it will return `None`. +pub fn for_type(ty: &hir::Type, db: &RootDatabase, edition: Edition) -> Option { + let ty = ty.strip_references(); + name_of_type(&ty, db, edition) +} + /// Suggest a unique name for generic parameter. /// /// `existing_params` is used to check if the name conflicts with existing @@ -269,10 +284,9 @@ fn var_name_from_pat(pat: &ast::Pat) -> Option { fn from_type(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option { let ty = sema.type_of_expr(expr)?.adjusted(); - let ty = ty.remove_ref().unwrap_or(ty); let edition = sema.scope(expr.syntax())?.krate().edition(sema.db); - name_of_type(&ty, sema.db, edition) + for_type(&ty, sema.db, edition) } fn name_of_type(ty: &hir::Type, db: &RootDatabase, edition: Edition) -> Option {