summaryrefslogtreecommitdiffstats
path: root/src/tools/rust-analyzer/crates/ide-completion/src
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/rust-analyzer/crates/ide-completion/src')
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs124
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs70
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs20
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs69
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render.rs42
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs24
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs26
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs28
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs21
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs13
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs77
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs54
23 files changed, 407 insertions, 190 deletions
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 72579e602..55c3e2839 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs
@@ -617,7 +617,6 @@ pub(super) fn complete_name_ref(
dot::complete_undotted_self(acc, ctx, path_ctx, expr_ctx);
item_list::complete_item_list_in_expr(acc, ctx, path_ctx, expr_ctx);
- record::complete_record_expr_func_update(acc, ctx, path_ctx, expr_ctx);
snippet::complete_expr_snippet(acc, ctx, path_ctx, expr_ctx);
}
PathKind::Type { location } => {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
index cf40ca489..02004ff7b 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
@@ -19,7 +19,7 @@ pub(crate) fn complete_dot(
};
// Suggest .await syntax for types that implement Future trait
- if receiver_ty.impls_future(ctx.db) {
+ if receiver_ty.impls_into_future(ctx.db) {
let mut item =
CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), "await");
item.detail("expr.await");
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
index 5d0ddaaf2..588b52cc1 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
@@ -1,8 +1,10 @@
//! Completion of names from the current scope in expression position.
use hir::ScopeDef;
+use syntax::ast;
use crate::{
+ completions::record::add_default_update,
context::{ExprCtx, PathCompletionCtx, Qualified},
CompletionContext, Completions,
};
@@ -219,60 +221,90 @@ pub(crate) fn complete_expr_path(
_ => (),
});
- if is_func_update.is_none() {
- let mut add_keyword =
- |kw, snippet| acc.add_keyword_snippet_expr(ctx, incomplete_let, kw, snippet);
+ match is_func_update {
+ Some(record_expr) => {
+ let ty = ctx.sema.type_of_expr(&ast::Expr::RecordExpr(record_expr.clone()));
- if !in_block_expr {
- add_keyword("unsafe", "unsafe {\n $0\n}");
- }
- add_keyword("match", "match $1 {\n $0\n}");
- add_keyword("while", "while $1 {\n $0\n}");
- add_keyword("while let", "while let $1 = $2 {\n $0\n}");
- add_keyword("loop", "loop {\n $0\n}");
- if in_match_guard {
- add_keyword("if", "if $0");
- } else {
- add_keyword("if", "if $1 {\n $0\n}");
+ match ty.as_ref().and_then(|t| t.original.as_adt()) {
+ Some(hir::Adt::Union(_)) => (),
+ _ => {
+ cov_mark::hit!(functional_update);
+ let missing_fields =
+ ctx.sema.record_literal_missing_fields(record_expr);
+ if !missing_fields.is_empty() {
+ add_default_update(acc, ctx, ty);
+ }
+ }
+ };
}
- add_keyword("if let", "if let $1 = $2 {\n $0\n}");
- add_keyword("for", "for $1 in $2 {\n $0\n}");
- add_keyword("true", "true");
- add_keyword("false", "false");
+ None => {
+ let mut add_keyword = |kw, snippet| {
+ acc.add_keyword_snippet_expr(ctx, incomplete_let, kw, snippet)
+ };
- if in_condition || in_block_expr {
- add_keyword("let", "let");
- }
+ if !in_block_expr {
+ add_keyword("unsafe", "unsafe {\n $0\n}");
+ }
+ add_keyword("match", "match $1 {\n $0\n}");
+ add_keyword("while", "while $1 {\n $0\n}");
+ add_keyword("while let", "while let $1 = $2 {\n $0\n}");
+ add_keyword("loop", "loop {\n $0\n}");
+ if in_match_guard {
+ add_keyword("if", "if $0");
+ } else {
+ add_keyword("if", "if $1 {\n $0\n}");
+ }
+ add_keyword("if let", "if let $1 = $2 {\n $0\n}");
+ add_keyword("for", "for $1 in $2 {\n $0\n}");
+ add_keyword("true", "true");
+ add_keyword("false", "false");
- if after_if_expr {
- add_keyword("else", "else {\n $0\n}");
- add_keyword("else if", "else if $1 {\n $0\n}");
- }
+ if in_condition || in_block_expr {
+ add_keyword("let", "let");
+ }
- if wants_mut_token {
- add_keyword("mut", "mut ");
- }
+ if after_if_expr {
+ add_keyword("else", "else {\n $0\n}");
+ add_keyword("else if", "else if $1 {\n $0\n}");
+ }
- if in_loop_body {
- if in_block_expr {
- add_keyword("continue", "continue;");
- add_keyword("break", "break;");
- } else {
- add_keyword("continue", "continue");
- add_keyword("break", "break");
+ if wants_mut_token {
+ add_keyword("mut", "mut ");
+ }
+
+ if in_loop_body {
+ if in_block_expr {
+ add_keyword("continue", "continue;");
+ add_keyword("break", "break;");
+ } else {
+ add_keyword("continue", "continue");
+ add_keyword("break", "break");
+ }
}
- }
- if let Some(ty) = innermost_ret_ty {
- add_keyword(
- "return",
- match (in_block_expr, ty.is_unit()) {
- (true, true) => "return ;",
- (true, false) => "return;",
- (false, true) => "return $0",
- (false, false) => "return",
- },
- );
+ if let Some(ret_ty) = innermost_ret_ty {
+ add_keyword(
+ "return",
+ match (ret_ty.is_unit(), in_block_expr) {
+ (true, true) => {
+ cov_mark::hit!(return_unit_block);
+ "return;"
+ }
+ (true, false) => {
+ cov_mark::hit!(return_unit_no_block);
+ "return"
+ }
+ (false, true) => {
+ cov_mark::hit!(return_value_block);
+ "return $0;"
+ }
+ (false, false) => {
+ cov_mark::hit!(return_value_no_block);
+ "return $0"
+ }
+ },
+ );
+ }
}
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
index e9256803c..785db6fde 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
@@ -233,7 +233,8 @@ fn add_type_alias_impl(
type_alias: hir::TypeAlias,
) {
let alias_name = type_alias.name(ctx.db);
- let (alias_name, escaped_name) = (alias_name.to_smol_str(), alias_name.escaped().to_smol_str());
+ let (alias_name, escaped_name) =
+ (alias_name.unescaped().to_smol_str(), alias_name.to_smol_str());
let label = format!("type {} =", alias_name);
let replacement = format!("type {} = ", escaped_name);
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs
index 3989a451b..1d03c8cc5 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs
@@ -75,16 +75,17 @@ impl Future for A {}
fn foo(a: A) { a.$0 }
"#,
expect![[r#"
- kw await expr.await
- sn box Box::new(expr)
- sn call function(expr)
- sn dbg dbg!(expr)
- sn dbgr dbg!(&expr)
- sn let let
- sn letm let mut
- sn match match expr {}
- sn ref &expr
- sn refm &mut expr
+ kw await expr.await
+ me into_future() (as IntoFuture) fn(self) -> <Self as IntoFuture>::IntoFuture
+ sn box Box::new(expr)
+ sn call function(expr)
+ sn dbg dbg!(expr)
+ sn dbgr dbg!(&expr)
+ sn let let
+ sn letm let mut
+ sn match match expr {}
+ sn ref &expr
+ sn refm &mut expr
"#]],
);
@@ -98,18 +99,45 @@ fn foo() {
}
"#,
expect![[r#"
- kw await expr.await
- sn box Box::new(expr)
- sn call function(expr)
- sn dbg dbg!(expr)
- sn dbgr dbg!(&expr)
- sn let let
- sn letm let mut
- sn match match expr {}
- sn ref &expr
- sn refm &mut expr
+ kw await expr.await
+ me into_future() (use core::future::IntoFuture) fn(self) -> <Self as IntoFuture>::IntoFuture
+ sn box Box::new(expr)
+ sn call function(expr)
+ sn dbg dbg!(expr)
+ sn dbgr dbg!(&expr)
+ sn let let
+ sn letm let mut
+ sn match match expr {}
+ sn ref &expr
+ sn refm &mut expr
"#]],
- )
+ );
+ }
+
+ #[test]
+ fn test_completion_await_impls_into_future() {
+ check(
+ r#"
+//- minicore: future
+use core::future::*;
+struct A {}
+impl IntoFuture for A {}
+fn foo(a: A) { a.$0 }
+"#,
+ expect![[r#"
+ kw await expr.await
+ me into_future() (as IntoFuture) fn(self) -> <Self as IntoFuture>::IntoFuture
+ sn box Box::new(expr)
+ sn call function(expr)
+ sn dbg dbg!(expr)
+ sn dbgr dbg!(&expr)
+ sn let let
+ sn letm let mut
+ sn match match expr {}
+ sn ref &expr
+ sn refm &mut expr
+ "#]],
+ );
}
#[test]
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs
index 9c975b929..950731eb4 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs
@@ -53,6 +53,7 @@ pub(crate) fn complete_mod(
let existing_mod_declarations = current_module
.children(ctx.db)
.filter_map(|module| Some(module.name(ctx.db)?.to_string()))
+ .filter(|module| module != ctx.original_token.text())
.collect::<FxHashSet<_>>();
let module_declaration_file =
@@ -351,4 +352,23 @@ fn ignored_bar() {}
"#]],
);
}
+
+ #[test]
+ fn semi_colon_completion() {
+ check(
+ r#"
+//- /lib.rs
+mod foo;
+//- /foo.rs
+mod bar {
+ mod baz$0
+}
+//- /foo/bar/baz.rs
+fn baz() {}
+"#,
+ expect![[r#"
+ md baz;
+ "#]],
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs
index 6b94347e0..b273a4cb5 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs
@@ -173,7 +173,7 @@ impl FormatStrParser {
}
}
(State::Expr, ':') if chars.peek().copied() == Some(':') => {
- // path seperator
+ // path separator
current_expr.push_str("::");
chars.next();
}
@@ -185,7 +185,7 @@ impl FormatStrParser {
current_expr = String::new();
self.state = State::FormatOpts;
} else {
- // We're inside of braced expression, assume that it's a struct field name/value delimeter.
+ // We're inside of braced expression, assume that it's a struct field name/value delimiter.
current_expr.push(chr);
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs
index 1c9042390..5d96fbd30 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs
@@ -3,7 +3,7 @@ use ide_db::SymbolKind;
use syntax::ast::{self, Expr};
use crate::{
- context::{DotAccess, DotAccessKind, ExprCtx, PathCompletionCtx, PatternContext, Qualified},
+ context::{DotAccess, DotAccessKind, PatternContext},
CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance,
CompletionRelevancePostfixMatch, Completions,
};
@@ -14,7 +14,24 @@ pub(crate) fn complete_record_pattern_fields(
pattern_ctx: &PatternContext,
) {
if let PatternContext { record_pat: Some(record_pat), .. } = pattern_ctx {
- complete_fields(acc, ctx, ctx.sema.record_pattern_missing_fields(record_pat));
+ let ty = ctx.sema.type_of_pat(&ast::Pat::RecordPat(record_pat.clone()));
+ let missing_fields = match ty.as_ref().and_then(|t| t.original.as_adt()) {
+ Some(hir::Adt::Union(un)) => {
+ // ctx.sema.record_pat_missing_fields will always return
+ // an empty Vec on a union literal. This is normally
+ // reasonable, but here we'd like to present the full list
+ // of fields if the literal is empty.
+ let were_fields_specified =
+ record_pat.record_pat_field_list().and_then(|fl| fl.fields().next()).is_some();
+
+ match were_fields_specified {
+ false => un.fields(ctx.db).into_iter().map(|f| (f, f.ty(ctx.db))).collect(),
+ true => return,
+ }
+ }
+ _ => ctx.sema.record_pattern_missing_fields(record_pat),
+ };
+ complete_fields(acc, ctx, missing_fields);
}
}
@@ -42,8 +59,13 @@ pub(crate) fn complete_record_expr_fields(
}
_ => {
let missing_fields = ctx.sema.record_literal_missing_fields(record_expr);
- add_default_update(acc, ctx, ty, &missing_fields);
+
+ if !missing_fields.is_empty() {
+ cov_mark::hit!(functional_update_field);
+ add_default_update(acc, ctx, ty);
+ }
if dot_prefix {
+ cov_mark::hit!(functional_update_one_dot);
let mut item =
CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), "..");
item.insert_text(".");
@@ -56,41 +78,18 @@ pub(crate) fn complete_record_expr_fields(
complete_fields(acc, ctx, missing_fields);
}
-// FIXME: This should probably be part of complete_path_expr
-pub(crate) fn complete_record_expr_func_update(
- acc: &mut Completions,
- ctx: &CompletionContext<'_>,
- path_ctx: &PathCompletionCtx,
- expr_ctx: &ExprCtx,
-) {
- if !matches!(path_ctx.qualified, Qualified::No) {
- return;
- }
- if let ExprCtx { is_func_update: Some(record_expr), .. } = expr_ctx {
- let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_expr.clone()));
-
- match ty.as_ref().and_then(|t| t.original.as_adt()) {
- Some(hir::Adt::Union(_)) => (),
- _ => {
- let missing_fields = ctx.sema.record_literal_missing_fields(record_expr);
- add_default_update(acc, ctx, ty, &missing_fields);
- }
- };
- }
-}
-
-fn add_default_update(
+pub(crate) fn add_default_update(
acc: &mut Completions,
ctx: &CompletionContext<'_>,
ty: Option<hir::TypeInfo>,
- missing_fields: &[(hir::Field, hir::Type)],
) {
let default_trait = ctx.famous_defs().core_default_Default();
- let impl_default_trait = default_trait
+ let impls_default_trait = default_trait
.zip(ty.as_ref())
.map_or(false, |(default_trait, ty)| ty.original.impls_trait(ctx.db, default_trait, &[]));
- if impl_default_trait && !missing_fields.is_empty() {
+ if impls_default_trait {
// FIXME: This should make use of scope_def like completions so we get all the other goodies
+ // that is we should handle this like actually completing the default function
let completion_text = "..Default::default()";
let mut item = CompletionItem::new(SymbolKind::Field, ctx.source_range(), completion_text);
let completion_text =
@@ -130,7 +129,7 @@ mod tests {
#[test]
fn literal_struct_completion_edit() {
check_edit(
- "FooDesc {…}",
+ "FooDesc{}",
r#"
struct FooDesc { pub bar: bool }
@@ -155,7 +154,7 @@ fn baz() {
#[test]
fn literal_struct_impl_self_completion() {
check_edit(
- "Self {…}",
+ "Self{}",
r#"
struct Foo {
bar: u64,
@@ -181,7 +180,7 @@ impl Foo {
);
check_edit(
- "Self(…)",
+ "Self()",
r#"
mod submod {
pub struct Foo(pub u64);
@@ -210,7 +209,7 @@ impl submod::Foo {
#[test]
fn literal_struct_completion_from_sub_modules() {
check_edit(
- "submod::Struct {…}",
+ "submod::Struct{}",
r#"
mod submod {
pub struct Struct {
@@ -239,7 +238,7 @@ fn f() -> submod::Struct {
#[test]
fn literal_struct_complexion_module() {
check_edit(
- "FooDesc {…}",
+ "FooDesc{}",
r#"
mod _69latrick {
pub struct FooDesc { pub six: bool, pub neuf: Vec<String>, pub bar: bool }
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 e35f79d2b..a5e854b74 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
@@ -64,8 +64,11 @@ pub(crate) struct PathCompletionCtx {
pub(super) qualified: Qualified,
/// The parent of the path we are completing.
pub(super) parent: Option<ast::Path>,
+ #[allow(dead_code)]
/// The path of which we are completing the segment
pub(super) path: ast::Path,
+ /// The path of which we are completing the segment in the original file
+ pub(crate) original_path: Option<ast::Path>,
pub(super) kind: PathKind,
/// Whether the path segment has type args or not.
pub(super) has_type_args: bool,
@@ -134,6 +137,7 @@ pub(crate) struct ExprCtx {
pub(crate) in_condition: bool,
pub(crate) incomplete_let: bool,
pub(crate) ref_expr_parent: Option<ast::RefExpr>,
+ /// The surrounding RecordExpression we are completing a functional update
pub(crate) is_func_update: Option<ast::RecordExpr>,
pub(crate) self_param: Option<hir::SelfParam>,
pub(crate) innermost_ret_ty: Option<hir::Type>,
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 22ec7cead..01dd9a234 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
@@ -588,12 +588,15 @@ impl<'a> CompletionContext<'a> {
};
let path = segment.parent_path();
+ let original_path = find_node_in_file_compensated(sema, original_file, &path);
+
let mut path_ctx = PathCompletionCtx {
has_call_parens: false,
has_macro_bang: false,
qualified: Qualified::No,
parent: None,
path: path.clone(),
+ original_path,
kind: PathKind::Item { kind: ItemListKind::SourceFile },
has_type_args: false,
use_tree_parent: false,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
index 946134b0f..86302cb06 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
@@ -117,7 +117,7 @@ pub(crate) fn render_field(
) -> CompletionItem {
let is_deprecated = ctx.is_deprecated(field);
let name = field.name(ctx.db());
- let (name, escaped_name) = (name.to_smol_str(), name.escaped().to_smol_str());
+ let (name, escaped_name) = (name.unescaped().to_smol_str(), name.to_smol_str());
let mut item = CompletionItem::new(
SymbolKind::Field,
ctx.source_range(),
@@ -283,8 +283,8 @@ fn render_resolution_path(
let name = local_name.to_smol_str();
let mut item = render_resolution_simple_(ctx, &local_name, import_to_add, resolution);
- if local_name.escaped().is_escaped() {
- item.insert_text(local_name.escaped().to_smol_str());
+ if local_name.is_escaped() {
+ item.insert_text(local_name.to_smol_str());
}
// Add `<>` for generic types
let type_path_no_ty_args = matches!(
@@ -306,7 +306,7 @@ fn render_resolution_path(
item.lookup_by(name.clone())
.label(SmolStr::from_iter([&name, "<…>"]))
.trigger_call_info()
- .insert_snippet(cap, format!("{}<$0>", local_name.escaped()));
+ .insert_snippet(cap, format!("{}<$0>", local_name));
}
}
}
@@ -323,9 +323,7 @@ fn render_resolution_path(
..CompletionRelevance::default()
});
- if let Some(ref_match) = compute_ref_match(completion, &ty) {
- item.ref_match(ref_match, path_ctx.path.syntax().text_range().start());
- }
+ path_ref_match(completion, path_ctx, &ty, &mut item);
};
item
}
@@ -342,7 +340,8 @@ fn render_resolution_simple_(
let ctx = ctx.import_to_add(import_to_add);
let kind = res_to_kind(resolution);
- let mut item = CompletionItem::new(kind, ctx.source_range(), local_name.to_smol_str());
+ let mut item =
+ CompletionItem::new(kind, ctx.source_range(), local_name.unescaped().to_smol_str());
item.set_relevance(ctx.completion_relevance())
.set_documentation(scope_def_docs(db, resolution))
.set_deprecated(scope_def_is_deprecated(&ctx, resolution));
@@ -452,6 +451,29 @@ fn compute_ref_match(
None
}
+fn path_ref_match(
+ completion: &CompletionContext<'_>,
+ path_ctx: &PathCompletionCtx,
+ ty: &hir::Type,
+ item: &mut Builder,
+) {
+ if let Some(original_path) = &path_ctx.original_path {
+ // At least one char was typed by the user already, in that case look for the original path
+ if let Some(original_path) = completion.sema.original_ast_node(original_path.clone()) {
+ if let Some(ref_match) = compute_ref_match(completion, ty) {
+ item.ref_match(ref_match, original_path.syntax().text_range().start());
+ }
+ }
+ } else {
+ // completion requested on an empty identifier, there is no path here yet.
+ // FIXME: This might create inconsistent completions where we show a ref match in macro inputs
+ // as long as nothing was typed yet
+ if let Some(ref_match) = compute_ref_match(completion, ty) {
+ item.ref_match(ref_match, completion.position.offset);
+ }
+ }
+}
+
#[cfg(test)]
mod tests {
use std::cmp;
@@ -564,6 +586,7 @@ fn main() { Foo::Fo$0 }
kind: SymbolKind(
Variant,
),
+ lookup: "Foo{}",
detail: "Foo { x: i32, y: i32 }",
},
]
@@ -590,6 +613,7 @@ fn main() { Foo::Fo$0 }
kind: SymbolKind(
Variant,
),
+ lookup: "Foo()",
detail: "Foo(i32, i32)",
},
]
@@ -706,7 +730,7 @@ fn main() { let _: m::Spam = S$0 }
kind: SymbolKind(
Variant,
),
- lookup: "Spam::Bar(…)",
+ lookup: "Spam::Bar()",
detail: "m::Spam::Bar(i32)",
relevance: CompletionRelevance {
exact_name_match: false,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs
index a810eef18..93ea825e0 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs
@@ -13,7 +13,7 @@ pub(crate) fn render_const(ctx: RenderContext<'_>, const_: hir::Const) -> Option
fn render(ctx: RenderContext<'_>, const_: hir::Const) -> Option<CompletionItem> {
let db = ctx.db();
let name = const_.name(db)?;
- let (name, escaped_name) = (name.to_smol_str(), name.escaped().to_smol_str());
+ let (name, escaped_name) = (name.unescaped().to_smol_str(), name.to_smol_str());
let detail = const_.display(db).to_string();
let mut item = CompletionItem::new(SymbolKind::Const, ctx.source_range(), name.clone());
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs
index 4b5535718..376120846 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs
@@ -52,10 +52,10 @@ fn render(
let (call, escaped_call) = match &func_kind {
FuncKind::Method(_, Some(receiver)) => (
- format!("{}.{}", receiver, &name).into(),
- format!("{}.{}", receiver.escaped(), name.escaped()).into(),
+ format!("{}.{}", receiver.unescaped(), name.unescaped()).into(),
+ format!("{}.{}", receiver, name).into(),
),
- _ => (name.to_smol_str(), name.escaped().to_smol_str()),
+ _ => (name.unescaped().to_smol_str(), name.to_smol_str()),
};
let mut item = CompletionItem::new(
if func.self_param(db).is_some() {
@@ -79,24 +79,24 @@ fn render(
..ctx.completion_relevance()
});
- if let Some(ref_match) = compute_ref_match(completion, &ret_type) {
- match func_kind {
- FuncKind::Function(path_ctx) => {
- item.ref_match(ref_match, path_ctx.path.syntax().text_range().start());
- }
- FuncKind::Method(DotAccess { receiver: Some(receiver), .. }, _) => {
- if let Some(original_expr) = completion.sema.original_ast_node(receiver.clone()) {
+ match func_kind {
+ FuncKind::Function(path_ctx) => {
+ super::path_ref_match(completion, path_ctx, &ret_type, &mut item);
+ }
+ FuncKind::Method(DotAccess { receiver: Some(receiver), .. }, _) => {
+ if let Some(original_expr) = completion.sema.original_ast_node(receiver.clone()) {
+ if let Some(ref_match) = compute_ref_match(completion, &ret_type) {
item.ref_match(ref_match, original_expr.syntax().text_range().start());
}
}
- _ => (),
}
+ _ => (),
}
item.set_documentation(ctx.docs(func))
.set_deprecated(ctx.is_deprecated(func) || ctx.is_deprecated_assoc_item(func))
.detail(detail(db, func))
- .lookup_by(name.to_smol_str());
+ .lookup_by(name.unescaped().to_smol_str());
match ctx.completion.config.snippet_cap {
Some(cap) => {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs
index 91a253f8f..0c791ac57 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs
@@ -2,16 +2,15 @@
use hir::{db::HirDatabase, Documentation, HasAttrs, StructKind};
use ide_db::SymbolKind;
-use syntax::AstNode;
use crate::{
context::{CompletionContext, PathCompletionCtx, PathKind},
item::{Builder, CompletionItem},
render::{
- compute_ref_match, compute_type_match,
+ compute_type_match,
variant::{
- format_literal_label, render_record_lit, render_tuple_lit, visible_fields,
- RenderedLiteral,
+ format_literal_label, format_literal_lookup, render_record_lit, render_tuple_lit,
+ visible_fields, RenderedLiteral,
},
RenderContext,
},
@@ -73,7 +72,7 @@ fn render(
None => (name.clone().into(), name.into(), false),
};
let (qualified_name, escaped_qualified_name) =
- (qualified_name.to_string(), qualified_name.escaped().to_string());
+ (qualified_name.unescaped().to_string(), qualified_name.to_string());
let snippet_cap = ctx.snippet_cap();
let mut rendered = match kind {
@@ -97,13 +96,20 @@ fn render(
if !should_add_parens {
kind = StructKind::Unit;
}
+ let label = format_literal_label(&qualified_name, kind);
+ let lookup = if qualified {
+ format_literal_lookup(&short_qualified_name.to_string(), kind)
+ } else {
+ format_literal_lookup(&qualified_name, kind)
+ };
let mut item = CompletionItem::new(
CompletionItemKind::SymbolKind(thing.symbol_kind()),
ctx.source_range(),
- format_literal_label(&qualified_name, kind),
+ label,
);
+ item.lookup_by(lookup);
item.detail(rendered.detail);
match snippet_cap {
@@ -111,9 +117,6 @@ fn render(
None => item.insert_text(rendered.literal),
};
- if qualified {
- item.lookup_by(format_literal_label(&short_qualified_name.to_string(), kind));
- }
item.set_documentation(thing.docs(db)).set_deprecated(thing.is_deprecated(&ctx));
let ty = thing.ty(db);
@@ -121,9 +124,8 @@ fn render(
type_match: compute_type_match(ctx.completion, &ty),
..ctx.completion_relevance()
});
- if let Some(ref_match) = compute_ref_match(completion, &ty) {
- item.ref_match(ref_match, path_ctx.path.syntax().text_range().start());
- }
+
+ super::path_ref_match(completion, path_ctx, &ty, &mut item);
if let Some(import_to_add) = ctx.import_to_add {
item.add_import(import_to_add);
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs
index ca2269f13..eabd0bd17 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs
@@ -46,7 +46,7 @@ fn render(
ctx.source_range()
};
- let (name, escaped_name) = (name.to_smol_str(), name.escaped().to_smol_str());
+ let (name, escaped_name) = (name.unescaped().to_smol_str(), name.to_smol_str());
let docs = ctx.docs(macro_);
let docs_str = docs.as_ref().map(Documentation::as_str).unwrap_or_default();
let is_fn_like = macro_.is_fn_like(completion.db);
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs
index 34a384f2f..c845ff21a 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs
@@ -8,7 +8,7 @@ use syntax::SmolStr;
use crate::{
context::{ParamContext, ParamKind, PathCompletionCtx, PatternContext},
render::{
- variant::{format_literal_label, visible_fields},
+ variant::{format_literal_label, format_literal_lookup, visible_fields},
RenderContext,
},
CompletionItem, CompletionItemKind,
@@ -31,12 +31,13 @@ pub(crate) fn render_struct_pat(
}
let name = local_name.unwrap_or_else(|| strukt.name(ctx.db()));
- let (name, escaped_name) = (name.to_smol_str(), name.escaped().to_smol_str());
+ let (name, escaped_name) = (name.unescaped().to_smol_str(), name.to_smol_str());
let kind = strukt.kind(ctx.db());
let label = format_literal_label(name.as_str(), kind);
+ let lookup = format_literal_lookup(name.as_str(), kind);
let pat = render_pat(&ctx, pattern_ctx, &escaped_name, kind, &visible_fields, fields_omitted)?;
- Some(build_completion(ctx, label, pat, strukt))
+ Some(build_completion(ctx, label, lookup, pat, strukt))
}
pub(crate) fn render_variant_pat(
@@ -53,18 +54,21 @@ pub(crate) fn render_variant_pat(
let (visible_fields, fields_omitted) = visible_fields(ctx.completion, &fields, variant)?;
let (name, escaped_name) = match path {
- Some(path) => (path.to_string().into(), path.escaped().to_string().into()),
+ Some(path) => (path.unescaped().to_string().into(), path.to_string().into()),
None => {
let name = local_name.unwrap_or_else(|| variant.name(ctx.db()));
- (name.to_smol_str(), name.escaped().to_smol_str())
+ (name.unescaped().to_smol_str(), name.to_smol_str())
}
};
- let (label, pat) = match path_ctx {
- Some(PathCompletionCtx { has_call_parens: true, .. }) => (name, escaped_name.to_string()),
+ let (label, lookup, pat) = match path_ctx {
+ Some(PathCompletionCtx { has_call_parens: true, .. }) => {
+ (name.clone(), name, escaped_name.to_string())
+ }
_ => {
let kind = variant.kind(ctx.db());
let label = format_literal_label(name.as_str(), kind);
+ let lookup = format_literal_lookup(name.as_str(), kind);
let pat = render_pat(
&ctx,
pattern_ctx,
@@ -73,16 +77,17 @@ pub(crate) fn render_variant_pat(
&visible_fields,
fields_omitted,
)?;
- (label, pat)
+ (label, lookup, pat)
}
};
- Some(build_completion(ctx, label, pat, variant))
+ Some(build_completion(ctx, label, lookup, pat, variant))
}
fn build_completion(
ctx: RenderContext<'_>,
label: SmolStr,
+ lookup: SmolStr,
pat: String,
def: impl HasAttrs + Copy,
) -> CompletionItem {
@@ -90,6 +95,7 @@ fn build_completion(
item.set_documentation(ctx.docs(def))
.set_deprecated(ctx.is_deprecated(def))
.detail(&pat)
+ .lookup_by(lookup)
.set_relevance(ctx.completion_relevance());
match ctx.snippet_cap() {
Some(snippet_cap) => item.insert_snippet(snippet_cap, pat),
@@ -146,7 +152,7 @@ fn render_record_as_pat(
format!(
"{name} {{ {}{} }}",
fields.enumerate().format_with(", ", |(idx, field), f| {
- f(&format_args!("{}${}", field.name(db).escaped(), idx + 1))
+ f(&format_args!("{}${}", field.name(db), idx + 1))
}),
if fields_omitted { ", .." } else { "" },
name = name
@@ -155,7 +161,7 @@ fn render_record_as_pat(
None => {
format!(
"{name} {{ {}{} }}",
- fields.map(|field| field.name(db).escaped().to_smol_str()).format(", "),
+ fields.map(|field| field.name(db).to_smol_str()).format(", "),
if fields_omitted { ", .." } else { "" },
name = name
)
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs
index f1b23c76e..de919429f 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs
@@ -32,11 +32,11 @@ fn render(
let name = type_alias.name(db);
let (name, escaped_name) = if with_eq {
(
+ SmolStr::from_iter([&name.unescaped().to_smol_str(), " = "]),
SmolStr::from_iter([&name.to_smol_str(), " = "]),
- SmolStr::from_iter([&name.escaped().to_smol_str(), " = "]),
)
} else {
- (name.to_smol_str(), name.escaped().to_smol_str())
+ (name.unescaped().to_smol_str(), name.to_smol_str())
};
let detail = type_alias.display(db).to_string();
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs
index 9c9540a9b..54e97dd57 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs
@@ -6,7 +6,7 @@ use itertools::Itertools;
use crate::{
render::{
- variant::{format_literal_label, visible_fields},
+ variant::{format_literal_label, format_literal_lookup, visible_fields},
RenderContext,
},
CompletionItem, CompletionItemKind,
@@ -21,16 +21,19 @@ pub(crate) fn render_union_literal(
let name = local_name.unwrap_or_else(|| un.name(ctx.db()));
let (qualified_name, escaped_qualified_name) = match path {
- Some(p) => (p.to_string(), p.escaped().to_string()),
- None => (name.to_string(), name.escaped().to_string()),
+ Some(p) => (p.unescaped().to_string(), p.to_string()),
+ None => (name.unescaped().to_string(), name.to_string()),
};
-
+ let label = format_literal_label(&name.to_smol_str(), StructKind::Record);
+ let lookup = format_literal_lookup(&name.to_smol_str(), StructKind::Record);
let mut item = CompletionItem::new(
CompletionItemKind::SymbolKind(SymbolKind::Union),
ctx.source_range(),
- format_literal_label(&name.to_smol_str(), StructKind::Record),
+ label,
);
+ item.lookup_by(lookup);
+
let fields = un.fields(ctx.db());
let (fields, fields_omitted) = visible_fields(ctx.completion, &fields, un)?;
@@ -42,15 +45,15 @@ pub(crate) fn render_union_literal(
format!(
"{} {{ ${{1|{}|}}: ${{2:()}} }}$0",
escaped_qualified_name,
- fields.iter().map(|field| field.name(ctx.db()).escaped().to_smol_str()).format(",")
+ fields.iter().map(|field| field.name(ctx.db()).to_smol_str()).format(",")
)
} else {
format!(
"{} {{ {} }}",
escaped_qualified_name,
- fields.iter().format_with(", ", |field, f| {
- f(&format_args!("{}: ()", field.name(ctx.db()).escaped()))
- })
+ fields
+ .iter()
+ .format_with(", ", |field, f| { f(&format_args!("{}: ()", field.name(ctx.db()))) })
)
};
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs
index 003a0c11e..24e6abdc9 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs
@@ -24,9 +24,9 @@ pub(crate) fn render_record_lit(
) -> RenderedLiteral {
let completions = fields.iter().enumerate().format_with(", ", |(idx, field), f| {
if snippet_cap.is_some() {
- f(&format_args!("{}: ${{{}:()}}", field.name(db).escaped(), idx + 1))
+ f(&format_args!("{}: ${{{}:()}}", field.name(db), idx + 1))
} else {
- f(&format_args!("{}: ()", field.name(db).escaped()))
+ f(&format_args!("{}: ()", field.name(db)))
}
});
@@ -94,3 +94,12 @@ pub(crate) fn format_literal_label(name: &str, kind: StructKind) -> SmolStr {
StructKind::Unit => name.into(),
}
}
+
+/// Format a struct, etc. literal option for lookup used in completions filtering.
+pub(crate) fn format_literal_lookup(name: &str, kind: StructKind) -> SmolStr {
+ match kind {
+ StructKind::Tuple => SmolStr::from_iter([name, "()"]),
+ StructKind::Record => SmolStr::from_iter([name, "{}"]),
+ StructKind::Unit => name.into(),
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
index 925081ebf..8e26d889f 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
@@ -1,7 +1,7 @@
//! Completion tests for expressions.
use expect_test::{expect, Expect};
-use crate::tests::{completion_list, BASE_ITEMS_FIXTURE};
+use crate::tests::{check_edit, completion_list, BASE_ITEMS_FIXTURE};
fn check(ra_fixture: &str, expect: Expect) {
let actual = completion_list(&format!("{}{}", BASE_ITEMS_FIXTURE, ra_fixture));
@@ -670,3 +670,78 @@ fn main() {
"#]],
);
}
+
+#[test]
+fn varaiant_with_struct() {
+ check_empty(
+ r#"
+pub struct YoloVariant {
+ pub f: usize
+}
+
+pub enum HH {
+ Yolo(YoloVariant),
+}
+
+fn brr() {
+ let t = HH::Yolo(Y$0);
+}
+"#,
+ expect![[r#"
+ en HH
+ fn brr() fn()
+ st YoloVariant
+ st YoloVariant {…} YoloVariant { f: usize }
+ bt u32
+ kw crate::
+ kw false
+ kw for
+ kw if
+ kw if let
+ kw loop
+ kw match
+ kw return
+ kw self::
+ kw true
+ kw unsafe
+ kw while
+ kw while let
+ "#]],
+ );
+}
+
+#[test]
+fn return_unit_block() {
+ cov_mark::check!(return_unit_block);
+ check_edit("return", r#"fn f() { if true { $0 } }"#, r#"fn f() { if true { return; } }"#);
+}
+
+#[test]
+fn return_unit_no_block() {
+ cov_mark::check!(return_unit_no_block);
+ check_edit(
+ "return",
+ r#"fn f() { match () { () => $0 } }"#,
+ r#"fn f() { match () { () => return } }"#,
+ );
+}
+
+#[test]
+fn return_value_block() {
+ cov_mark::check!(return_value_block);
+ check_edit(
+ "return",
+ r#"fn f() -> i32 { if true { $0 } }"#,
+ r#"fn f() -> i32 { if true { return $0; } }"#,
+ );
+}
+
+#[test]
+fn return_value_no_block() {
+ cov_mark::check!(return_value_no_block);
+ check_edit(
+ "return",
+ r#"fn f() -> i32 { match () { () => $0 } }"#,
+ r#"fn f() -> i32 { match () { () => return $0 } }"#,
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs
index 0bba7f245..a63ef0068 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs
@@ -159,7 +159,7 @@ pub mod some_module {
pub struct ThiiiiiirdStruct;
// contains all letters from the query, but not in the beginning, displayed second
pub struct AfterThirdStruct;
- // contains all letters from the query in the begginning, displayed first
+ // contains all letters from the query in the beginning, displayed first
pub struct ThirdStruct;
}
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 30ddbe2dc..85c4dbd66 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
@@ -467,7 +467,7 @@ fn foo() {
fn completes_enum_variant_pat() {
cov_mark::check!(enum_variant_pattern_path);
check_edit(
- "RecordVariant {…}",
+ "RecordVariant{}",
r#"
enum Enum {
RecordVariant { field: u32 }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs
index f6accc68e..328faaa06 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs
@@ -103,8 +103,9 @@ fn foo(f: Struct) {
}
#[test]
-fn functional_update() {
- // FIXME: This should filter out all completions that do not have the type `Foo`
+fn in_functional_update() {
+ cov_mark::check!(functional_update);
+
check(
r#"
//- minicore:default
@@ -116,13 +117,21 @@ impl Default for Foo {
fn main() {
let thing = 1;
let foo = Foo { foo1: 0, foo2: 0 };
- let foo2 = Foo { thing, $0 }
+ let foo2 = Foo { thing, ..$0 }
}
"#,
expect![[r#"
fd ..Default::default()
- fd foo1 u32
- fd foo2 u32
+ fn main() fn()
+ lc foo Foo
+ lc thing i32
+ md core
+ st Foo
+ st Foo {…} Foo { foo1: u32, foo2: u32 }
+ tt Default
+ bt u32
+ kw crate::
+ kw self::
"#]],
);
check(
@@ -136,14 +145,19 @@ impl Default for Foo {
fn main() {
let thing = 1;
let foo = Foo { foo1: 0, foo2: 0 };
- let foo2 = Foo { thing, .$0 }
+ let foo2 = Foo { thing, ..Default::$0 }
}
"#,
expect![[r#"
- fd ..Default::default()
- sn ..
+ fn default() (as Default) fn() -> Self
"#]],
);
+}
+
+#[test]
+fn functional_update_no_dot() {
+ cov_mark::check!(functional_update_field);
+ // FIXME: This should filter out all completions that do not have the type `Foo`
check(
r#"
//- minicore:default
@@ -155,23 +169,20 @@ impl Default for Foo {
fn main() {
let thing = 1;
let foo = Foo { foo1: 0, foo2: 0 };
- let foo2 = Foo { thing, ..$0 }
+ let foo2 = Foo { thing, $0 }
}
"#,
expect![[r#"
fd ..Default::default()
- fn main() fn()
- lc foo Foo
- lc thing i32
- md core
- st Foo
- st Foo {…} Foo { foo1: u32, foo2: u32 }
- tt Default
- bt u32
- kw crate::
- kw self::
+ fd foo1 u32
+ fd foo2 u32
"#]],
);
+}
+
+#[test]
+fn functional_update_one_dot() {
+ cov_mark::check!(functional_update_one_dot);
check(
r#"
//- minicore:default
@@ -183,11 +194,12 @@ impl Default for Foo {
fn main() {
let thing = 1;
let foo = Foo { foo1: 0, foo2: 0 };
- let foo2 = Foo { thing, ..Default::$0 }
+ let foo2 = Foo { thing, .$0 }
}
"#,
expect![[r#"
- fn default() (as Default) fn() -> Self
+ fd ..Default::default()
+ sn ..
"#]],
);
}