Skip to content

Commit 6faf409

Browse files
committed
Auto merge of rust-lang#18031 - roife:suggest-name-in-completion, r=Veykril
feat: Suggest name in completion for let_stmt and fn_param fix rust-lang#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.
2 parents c2f9b47 + ef491f2 commit 6faf409

File tree

12 files changed

+142
-15
lines changed

12 files changed

+142
-15
lines changed

src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
use hir::TypeInfo;
2+
use ide_db::syntax_helpers::suggest_name;
23
use syntax::{
34
ast::{self, edit::IndentLevel, edit_in_place::Indent, make, AstNode, HasName},
45
ted, NodeOrToken,
56
SyntaxKind::{BLOCK_EXPR, BREAK_EXPR, COMMENT, LOOP_EXPR, MATCH_GUARD, PATH_EXPR, RETURN_EXPR},
67
SyntaxNode, T,
78
};
89

9-
use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists};
10+
use crate::{AssistContext, AssistId, AssistKind, Assists};
1011

1112
// Assist: extract_variable
1213
//

src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ use std::ops::Not;
22

33
use crate::{
44
assist_context::{AssistContext, Assists},
5-
utils::{convert_param_list_to_arg_list, suggest_name},
5+
utils::convert_param_list_to_arg_list,
66
};
77
use either::Either;
88
use hir::{db::HirDatabase, HasVisibility};
99
use ide_db::{
1010
assists::{AssistId, GroupLabel},
1111
path_transform::PathTransform,
12+
syntax_helpers::suggest_name,
1213
FxHashMap, FxHashSet,
1314
};
1415
use itertools::Itertools;

src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
use ide_db::syntax_helpers::suggest_name;
12
use syntax::{
23
ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode, HasGenericParams},
34
ted,
45
};
56

6-
use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists};
7+
use crate::{AssistContext, AssistId, AssistKind, Assists};
78

89
// Assist: introduce_named_generic
910
//

src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
use ide_db::syntax_helpers::suggest_name;
12
use syntax::{
23
ast::{self, make, AstNode},
34
ted,
45
};
56

6-
use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists};
7+
use crate::{AssistContext, AssistId, AssistKind, Assists};
78

89
// Assist: replace_is_some_with_if_let_some
910
//

src/tools/rust-analyzer/crates/ide-assists/src/utils.rs

-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ use crate::assist_context::{AssistContext, SourceChangeBuilder};
2323

2424
mod gen_trait_fn_body;
2525
pub(crate) mod ref_field_expr;
26-
pub(crate) mod suggest_name;
2726

2827
pub(crate) fn unwrap_trivial_block(block_expr: ast::BlockExpr) -> ast::Expr {
2928
extract_trivial_expression(&block_expr)

src/tools/rust-analyzer/crates/ide-completion/src/completions.rs

+10
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,16 @@ impl Completions {
617617
}
618618
self.add_opt(render_struct_pat(RenderContext::new(ctx), pattern_ctx, strukt, local_name));
619619
}
620+
621+
pub(crate) fn suggest_name(&mut self, ctx: &CompletionContext<'_>, name: &str) {
622+
let item = CompletionItem::new(
623+
CompletionItemKind::Binding,
624+
ctx.source_range(),
625+
SmolStr::from(name),
626+
ctx.edition,
627+
);
628+
item.add_to(self, ctx.db);
629+
}
620630
}
621631

622632
/// Calls the callback for each variant of the provided enum with the path to the variant.

src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs

+13
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Completes constants and paths in unqualified patterns.
22
33
use hir::{db::DefDatabase, AssocItem, ScopeDef};
4+
use ide_db::syntax_helpers::suggest_name;
45
use syntax::ast::Pat;
56

67
use crate::{
@@ -45,6 +46,18 @@ pub(crate) fn complete_pattern(
4546
return;
4647
}
4748

49+
// Suggest name only in let-stmt and fn param
50+
if pattern_ctx.should_suggest_name {
51+
if let Some(suggested) = ctx
52+
.expected_type
53+
.as_ref()
54+
.map(|ty| ty.strip_references())
55+
.and_then(|ty| suggest_name::for_type(&ty, ctx.db, ctx.edition))
56+
{
57+
acc.suggest_name(ctx, &suggested);
58+
}
59+
}
60+
4861
let refutable = pattern_ctx.refutability == PatternRefutability::Refutable;
4962
let single_variant_enum = |enum_: hir::Enum| ctx.db.enum_data(enum_.into()).variants.len() == 1;
5063

src/tools/rust-analyzer/crates/ide-completion/src/context.rs

+1
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ pub(crate) struct PatternContext {
264264
pub(crate) refutability: PatternRefutability,
265265
pub(crate) param_ctx: Option<ParamContext>,
266266
pub(crate) has_type_ascription: bool,
267+
pub(crate) should_suggest_name: bool,
267268
pub(crate) parent_pat: Option<ast::Pat>,
268269
pub(crate) ref_token: Option<SyntaxToken>,
269270
pub(crate) mut_token: Option<SyntaxToken>,

src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs

+13
Original file line numberDiff line numberDiff line change
@@ -1430,10 +1430,23 @@ fn pattern_context_for(
14301430
_ => (None, None),
14311431
};
14321432

1433+
// Only suggest name in let-stmt or fn param
1434+
let should_suggest_name = matches!(
1435+
&pat,
1436+
ast::Pat::IdentPat(it)
1437+
if it.syntax()
1438+
.parent()
1439+
.map_or(false, |node| {
1440+
let kind = node.kind();
1441+
ast::LetStmt::can_cast(kind) || ast::Param::can_cast(kind)
1442+
})
1443+
);
1444+
14331445
PatternContext {
14341446
refutability,
14351447
param_ctx,
14361448
has_type_ascription,
1449+
should_suggest_name,
14371450
parent_pat: pat.syntax().parent().and_then(ast::Pat::cast),
14381451
mut_token,
14391452
ref_token,

src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs

+73
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ fn foo(a$0: Tuple) {
198198
st Unit
199199
bn Record {…} Record { field$1 }$0
200200
bn Tuple(…) Tuple($1)$0
201+
bn tuple
201202
kw mut
202203
kw ref
203204
"#]],
@@ -850,3 +851,75 @@ fn foo() {
850851
"#,
851852
);
852853
}
854+
855+
#[test]
856+
fn suggest_name_for_pattern() {
857+
check_edit(
858+
"s1",
859+
r#"
860+
struct S1;
861+
862+
fn foo() {
863+
let $0 = S1;
864+
}
865+
"#,
866+
r#"
867+
struct S1;
868+
869+
fn foo() {
870+
let s1 = S1;
871+
}
872+
"#,
873+
);
874+
875+
check_edit(
876+
"s1",
877+
r#"
878+
struct S1;
879+
880+
fn foo(s$0: S1) {
881+
}
882+
"#,
883+
r#"
884+
struct S1;
885+
886+
fn foo(s1: S1) {
887+
}
888+
"#,
889+
);
890+
891+
// Tests for &adt
892+
check_edit(
893+
"s1",
894+
r#"
895+
struct S1;
896+
897+
fn foo() {
898+
let $0 = &S1;
899+
}
900+
"#,
901+
r#"
902+
struct S1;
903+
904+
fn foo() {
905+
let s1 = &S1;
906+
}
907+
"#,
908+
);
909+
910+
// Do not suggest reserved keywords
911+
check_empty(
912+
r#"
913+
struct Struct;
914+
915+
fn foo() {
916+
let $0 = Struct;
917+
}
918+
"#,
919+
expect![[r#"
920+
st Struct
921+
kw mut
922+
kw ref
923+
"#]],
924+
);
925+
}

src/tools/rust-analyzer/crates/ide-db/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ pub mod syntax_helpers {
3838
pub mod format_string_exprs;
3939
pub use hir::insert_whitespace_into_node;
4040
pub mod node_ext;
41+
pub mod suggest_name;
4142

4243
pub use parser::LexedStr;
4344
}

src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs renamed to src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs

+23-10
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
//! This module contains functions to suggest names for expressions, functions and other items
22
33
use hir::Semantics;
4-
use ide_db::{FxHashSet, RootDatabase};
54
use itertools::Itertools;
5+
use rustc_hash::FxHashSet;
66
use stdx::to_lower_snake_case;
77
use syntax::{
88
ast::{self, HasName},
99
match_ast, AstNode, Edition, SmolStr,
1010
};
1111

12+
use crate::RootDatabase;
13+
1214
/// Trait names, that will be ignored when in `impl Trait` and `dyn Trait`
1315
const USELESS_TRAITS: &[&str] = &["Send", "Sync", "Copy", "Clone", "Eq", "PartialEq"];
1416

@@ -58,6 +60,21 @@ const USELESS_METHODS: &[&str] = &[
5860
"into_future",
5961
];
6062

63+
/// Suggest a name for given type.
64+
///
65+
/// The function will strip references first, and suggest name from the inner type.
66+
///
67+
/// - If `ty` is an ADT, it will suggest the name of the ADT.
68+
/// + If `ty` is wrapped in `Box`, `Option` or `Result`, it will suggest the name from the inner type.
69+
/// - If `ty` is a trait, it will suggest the name of the trait.
70+
/// - If `ty` is an `impl Trait`, it will suggest the name of the first trait.
71+
///
72+
/// If the suggested name conflicts with reserved keywords, it will return `None`.
73+
pub fn for_type(ty: &hir::Type, db: &RootDatabase, edition: Edition) -> Option<String> {
74+
let ty = ty.strip_references();
75+
name_of_type(&ty, db, edition)
76+
}
77+
6178
/// Suggest a unique name for generic parameter.
6279
///
6380
/// `existing_params` is used to check if the name conflicts with existing
@@ -66,10 +83,7 @@ const USELESS_METHODS: &[&str] = &[
6683
/// The function checks if the name conflicts with existing generic parameters.
6784
/// If so, it will try to resolve the conflict by adding a number suffix, e.g.
6885
/// `T`, `T0`, `T1`, ...
69-
pub(crate) fn for_unique_generic_name(
70-
name: &str,
71-
existing_params: &ast::GenericParamList,
72-
) -> SmolStr {
86+
pub fn for_unique_generic_name(name: &str, existing_params: &ast::GenericParamList) -> SmolStr {
7387
let param_names = existing_params
7488
.generic_params()
7589
.map(|param| match param {
@@ -101,7 +115,7 @@ pub(crate) fn for_unique_generic_name(
101115
///
102116
/// If the name conflicts with existing generic parameters, it will try to
103117
/// resolve the conflict with `for_unique_generic_name`.
104-
pub(crate) fn for_impl_trait_as_generic(
118+
pub fn for_impl_trait_as_generic(
105119
ty: &ast::ImplTraitType,
106120
existing_params: &ast::GenericParamList,
107121
) -> SmolStr {
@@ -132,7 +146,7 @@ pub(crate) fn for_impl_trait_as_generic(
132146
///
133147
/// Currently it sticks to the first name found.
134148
// FIXME: Microoptimize and return a `SmolStr` here.
135-
pub(crate) fn for_variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String {
149+
pub fn for_variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String {
136150
// `from_param` does not benefit from stripping
137151
// it need the largest context possible
138152
// so we check firstmost
@@ -184,7 +198,7 @@ fn normalize(name: &str) -> Option<String> {
184198

185199
fn is_valid_name(name: &str) -> bool {
186200
matches!(
187-
ide_db::syntax_helpers::LexedStr::single_token(syntax::Edition::CURRENT_FIXME, name),
201+
super::LexedStr::single_token(syntax::Edition::CURRENT_FIXME, name),
188202
Some((syntax::SyntaxKind::IDENT, _error))
189203
)
190204
}
@@ -270,10 +284,9 @@ fn var_name_from_pat(pat: &ast::Pat) -> Option<ast::Name> {
270284

271285
fn from_type(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<String> {
272286
let ty = sema.type_of_expr(expr)?.adjusted();
273-
let ty = ty.remove_ref().unwrap_or(ty);
274287
let edition = sema.scope(expr.syntax())?.krate().edition(sema.db);
275288

276-
name_of_type(&ty, sema.db, edition)
289+
for_type(&ty, sema.db, edition)
277290
}
278291

279292
fn name_of_type(ty: &hir::Type, db: &RootDatabase, edition: Edition) -> Option<String> {

0 commit comments

Comments
 (0)