about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-09-03 05:45:53 +0000
committerbors <bors@rust-lang.org>2024-09-03 05:45:53 +0000
commit6faf40992e166e0086e59e4353abcfed47e0143c (patch)
tree8c3127a23dd68d0fc163be9af097c7169d1296ec
parentc2f9b47c21bb8faaaee9eb3452c3f5723a2ba9b7 (diff)
parentef491f22999bb964ac92b64935d7ffe722e9c5a8 (diff)
downloadrust-6faf40992e166e0086e59e4353abcfed47e0143c.tar.gz
rust-6faf40992e166e0086e59e4353abcfed47e0143c.zip
Auto merge of #18031 - roife:suggest-name-in-completion, r=Veykril
feat: Suggest name in completion for let_stmt and fn_param

fix #17780

1. Refactor: move `ide_assist::utils::suggest_name` to `ide-db::syntax_helpers::suggest_name` for reuse.
2. When completing `IdentPat`, detecte if the current node is a `let_stmt` or `fn_param`, and suggesting a new name based on the context.
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/utils.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs13
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs13
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs73
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/lib.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs (renamed from src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs)33
12 files changed, 142 insertions, 15 deletions
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
index 5ae75bb1ff8..a43a4b5e1a0 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
@@ -1,4 +1,5 @@
 use hir::TypeInfo;
+use ide_db::syntax_helpers::suggest_name;
 use syntax::{
     ast::{self, edit::IndentLevel, edit_in_place::Indent, make, AstNode, HasName},
     ted, NodeOrToken,
@@ -6,7 +7,7 @@ use syntax::{
     SyntaxNode, T,
 };
 
-use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists};
+use crate::{AssistContext, AssistId, AssistKind, Assists};
 
 // Assist: extract_variable
 //
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs
index bf4ce5c907e..c22d19574fb 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs
@@ -2,13 +2,14 @@ use std::ops::Not;
 
 use crate::{
     assist_context::{AssistContext, Assists},
-    utils::{convert_param_list_to_arg_list, suggest_name},
+    utils::convert_param_list_to_arg_list,
 };
 use either::Either;
 use hir::{db::HirDatabase, HasVisibility};
 use ide_db::{
     assists::{AssistId, GroupLabel},
     path_transform::PathTransform,
+    syntax_helpers::suggest_name,
     FxHashMap, FxHashSet,
 };
 use itertools::Itertools;
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs
index 543b7f7ab63..a734a6cc2bc 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs
@@ -1,9 +1,10 @@
+use ide_db::syntax_helpers::suggest_name;
 use syntax::{
     ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode, HasGenericParams},
     ted,
 };
 
-use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists};
+use crate::{AssistContext, AssistId, AssistKind, Assists};
 
 // Assist: introduce_named_generic
 //
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs
index 59bb0c45e14..a856da09215 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs
@@ -1,9 +1,10 @@
+use ide_db::syntax_helpers::suggest_name;
 use syntax::{
     ast::{self, make, AstNode},
     ted,
 };
 
-use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists};
+use crate::{AssistContext, AssistId, AssistKind, Assists};
 
 // Assist: replace_is_some_with_if_let_some
 //
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
index b8a6f3b6dbe..19d1ef3157d 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
@@ -23,7 +23,6 @@ use crate::assist_context::{AssistContext, SourceChangeBuilder};
 
 mod gen_trait_fn_body;
 pub(crate) mod ref_field_expr;
-pub(crate) mod suggest_name;
 
 pub(crate) fn unwrap_trivial_block(block_expr: ast::BlockExpr) -> ast::Expr {
     extract_trivial_expression(&block_expr)
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs
index b537150608b..414627fbaba 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs
+++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs
index 60cfb7e5a8c..2a06fc40175 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs
+++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
index bcd9df94194..d457ba32bf0 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
@@ -264,6 +264,7 @@ pub(crate) struct PatternContext {
     pub(crate) refutability: PatternRefutability,
     pub(crate) param_ctx: Option<ParamContext>,
     pub(crate) has_type_ascription: bool,
+    pub(crate) should_suggest_name: bool,
     pub(crate) parent_pat: Option<ast::Pat>,
     pub(crate) ref_token: Option<SyntaxToken>,
     pub(crate) mut_token: Option<SyntaxToken>,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
index 292c419498d..1f9e3edf625 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
+++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs
index 6a0b67e291a..bd3e7c72bcd 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs
@@ -198,6 +198,7 @@ fn foo(a$0: Tuple) {
             st Unit
             bn Record {…} Record { field$1 }$0
             bn Tuple(…)   Tuple($1)$0
+            bn tuple
             kw mut
             kw ref
         "#]],
@@ -850,3 +851,75 @@ fn foo() {
 "#,
     );
 }
+
+#[test]
+fn suggest_name_for_pattern() {
+    check_edit(
+        "s1",
+        r#"
+struct S1;
+
+fn foo() {
+    let $0 = S1;
+}
+"#,
+        r#"
+struct S1;
+
+fn foo() {
+    let s1 = S1;
+}
+"#,
+    );
+
+    check_edit(
+        "s1",
+        r#"
+struct S1;
+
+fn foo(s$0: S1) {
+}
+"#,
+        r#"
+struct S1;
+
+fn foo(s1: S1) {
+}
+"#,
+    );
+
+    // Tests for &adt
+    check_edit(
+        "s1",
+        r#"
+struct S1;
+
+fn foo() {
+    let $0 = &S1;
+}
+"#,
+        r#"
+struct S1;
+
+fn foo() {
+    let s1 = &S1;
+}
+"#,
+    );
+
+    // Do not suggest reserved keywords
+    check_empty(
+        r#"
+struct Struct;
+
+fn foo() {
+    let $0 = Struct;
+}
+"#,
+        expect![[r#"
+            st Struct
+            kw mut
+            kw ref
+        "#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
index 8a2068e9039..ab161f0ce57 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
@@ -38,6 +38,7 @@ pub mod syntax_helpers {
     pub mod format_string_exprs;
     pub use hir::insert_whitespace_into_node;
     pub mod node_ext;
+    pub mod suggest_name;
 
     pub use parser::LexedStr;
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs
index 3130ef06955..6ee526a67ea 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs
@@ -1,14 +1,16 @@
 //! This module contains functions to suggest names for expressions, functions and other items
 
 use hir::Semantics;
-use ide_db::{FxHashSet, RootDatabase};
 use itertools::Itertools;
+use rustc_hash::FxHashSet;
 use stdx::to_lower_snake_case;
 use syntax::{
     ast::{self, HasName},
     match_ast, AstNode, Edition, SmolStr,
 };
 
+use crate::RootDatabase;
+
 /// Trait names, that will be ignored when in `impl Trait` and `dyn Trait`
 const USELESS_TRAITS: &[&str] = &["Send", "Sync", "Copy", "Clone", "Eq", "PartialEq"];
 
@@ -58,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<String> {
+    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
@@ -66,10 +83,7 @@ const USELESS_METHODS: &[&str] = &[
 /// The function checks if the name conflicts with existing generic parameters.
 /// If so, it will try to resolve the conflict by adding a number suffix, e.g.
 /// `T`, `T0`, `T1`, ...
-pub(crate) fn for_unique_generic_name(
-    name: &str,
-    existing_params: &ast::GenericParamList,
-) -> SmolStr {
+pub fn for_unique_generic_name(name: &str, existing_params: &ast::GenericParamList) -> SmolStr {
     let param_names = existing_params
         .generic_params()
         .map(|param| match param {
@@ -101,7 +115,7 @@ pub(crate) fn for_unique_generic_name(
 ///
 /// If the name conflicts with existing generic parameters, it will try to
 /// resolve the conflict with `for_unique_generic_name`.
-pub(crate) fn for_impl_trait_as_generic(
+pub fn for_impl_trait_as_generic(
     ty: &ast::ImplTraitType,
     existing_params: &ast::GenericParamList,
 ) -> SmolStr {
@@ -132,7 +146,7 @@ pub(crate) fn for_impl_trait_as_generic(
 ///
 /// Currently it sticks to the first name found.
 // FIXME: Microoptimize and return a `SmolStr` here.
-pub(crate) fn for_variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String {
+pub fn for_variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String {
     // `from_param` does not benefit from stripping
     // it need the largest context possible
     // so we check firstmost
@@ -184,7 +198,7 @@ fn normalize(name: &str) -> Option<String> {
 
 fn is_valid_name(name: &str) -> bool {
     matches!(
-        ide_db::syntax_helpers::LexedStr::single_token(syntax::Edition::CURRENT_FIXME, name),
+        super::LexedStr::single_token(syntax::Edition::CURRENT_FIXME, name),
         Some((syntax::SyntaxKind::IDENT, _error))
     )
 }
@@ -270,10 +284,9 @@ fn var_name_from_pat(pat: &ast::Pat) -> Option<ast::Name> {
 
 fn from_type(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<String> {
     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<String> {