diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:57:19 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:57:19 +0000 |
commit | a0b8f38ab54ac451646aa00cd5e91b6c76f22a84 (patch) | |
tree | fc451898ccaf445814e26b46664d78702178101d /src/tools/rust-analyzer/crates/ide-completion | |
parent | Adding debian version 1.71.1+dfsg1-2. (diff) | |
download | rustc-a0b8f38ab54ac451646aa00cd5e91b6c76f22a84.tar.xz rustc-a0b8f38ab54ac451646aa00cd5e91b6c76f22a84.zip |
Merging upstream version 1.72.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tools/rust-analyzer/crates/ide-completion')
48 files changed, 1393 insertions, 273 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 c3136f6df..480cb77b4 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -23,8 +23,8 @@ pub(crate) mod env_vars; use std::iter; -use hir::{known, ScopeDef, Variant}; -use ide_db::{imports::import_assets::LocatedImport, SymbolKind}; +use hir::{known, HasAttrs, ScopeDef, Variant}; +use ide_db::{imports::import_assets::LocatedImport, RootDatabase, SymbolKind}; use syntax::ast; use crate::{ @@ -62,8 +62,8 @@ impl From<Completions> for Vec<CompletionItem> { impl Builder { /// Convenience method, which allows to add a freshly created completion into accumulator /// without binding it to the variable. - pub(crate) fn add_to(self, acc: &mut Completions) { - acc.add(self.build()) + pub(crate) fn add_to(self, acc: &mut Completions, db: &RootDatabase) { + acc.add(self.build(db)) } } @@ -78,17 +78,9 @@ impl Completions { } } - pub(crate) fn add_all<I>(&mut self, items: I) - where - I: IntoIterator, - I::Item: Into<CompletionItem>, - { - items.into_iter().for_each(|item| self.add(item.into())) - } - pub(crate) fn add_keyword(&mut self, ctx: &CompletionContext<'_>, keyword: &'static str) { let item = CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), keyword); - item.add_to(self); + item.add_to(self, ctx.db); } pub(crate) fn add_nameref_keywords_with_colon(&mut self, ctx: &CompletionContext<'_>) { @@ -142,7 +134,7 @@ impl Completions { item.insert_text(if snippet.contains('$') { kw } else { snippet }); } }; - item.add_to(self); + item.add_to(self, ctx.db); } pub(crate) fn add_keyword_snippet( @@ -157,7 +149,7 @@ impl Completions { Some(cap) => item.insert_snippet(cap, snippet), None => item.insert_text(if snippet.contains('$') { kw } else { snippet }), }; - item.add_to(self); + item.add_to(self, ctx.db); } pub(crate) fn add_crate_roots( @@ -165,9 +157,9 @@ impl Completions { ctx: &CompletionContext<'_>, path_ctx: &PathCompletionCtx, ) { - ctx.process_all_names(&mut |name, res| match res { - ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) if m.is_crate_root(ctx.db) => { - self.add_module(ctx, path_ctx, m, name); + ctx.process_all_names(&mut |name, res, doc_aliases| match res { + ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) if m.is_crate_root() => { + self.add_module(ctx, path_ctx, m, name, doc_aliases); } _ => (), }); @@ -179,7 +171,11 @@ impl Completions { path_ctx: &PathCompletionCtx, local_name: hir::Name, resolution: hir::ScopeDef, + doc_aliases: Vec<syntax::SmolStr>, ) { + if !ctx.check_stability(resolution.attrs(ctx.db).as_deref()) { + return; + } let is_private_editable = match ctx.def_is_visible(&resolution) { Visible::Yes => false, Visible::Editable => true, @@ -187,12 +183,14 @@ impl Completions { }; self.add( render_path_resolution( - RenderContext::new(ctx).private_editable(is_private_editable), + RenderContext::new(ctx) + .private_editable(is_private_editable) + .doc_aliases(doc_aliases), path_ctx, local_name, resolution, ) - .build(), + .build(ctx.db), ); } @@ -203,6 +201,9 @@ impl Completions { local_name: hir::Name, resolution: hir::ScopeDef, ) { + if !ctx.check_stability(resolution.attrs(ctx.db).as_deref()) { + return; + } let is_private_editable = match ctx.def_is_visible(&resolution) { Visible::Yes => false, Visible::Editable => true, @@ -215,7 +216,7 @@ impl Completions { local_name, resolution, ) - .build(), + .build(ctx.db), ); } @@ -225,6 +226,9 @@ impl Completions { path_ctx: &PathCompletionCtx, e: hir::Enum, ) { + if !ctx.check_stability(Some(&e.attrs(ctx.db))) { + return; + } e.variants(ctx.db) .into_iter() .for_each(|variant| self.add_enum_variant(ctx, path_ctx, variant, None)); @@ -236,12 +240,17 @@ impl Completions { path_ctx: &PathCompletionCtx, module: hir::Module, local_name: hir::Name, + doc_aliases: Vec<syntax::SmolStr>, ) { + if !ctx.check_stability(Some(&module.attrs(ctx.db))) { + return; + } self.add_path_resolution( ctx, path_ctx, local_name, hir::ScopeDef::ModuleDef(module.into()), + doc_aliases, ); } @@ -252,6 +261,9 @@ impl Completions { mac: hir::Macro, local_name: hir::Name, ) { + if !ctx.check_stability(Some(&mac.attrs(ctx.db))) { + return; + } let is_private_editable = match ctx.is_visible(&mac) { Visible::Yes => false, Visible::Editable => true, @@ -264,7 +276,7 @@ impl Completions { local_name, mac, ) - .build(), + .build(ctx.db), ); } @@ -275,19 +287,25 @@ impl Completions { func: hir::Function, local_name: Option<hir::Name>, ) { + if !ctx.check_stability(Some(&func.attrs(ctx.db))) { + return; + } let is_private_editable = match ctx.is_visible(&func) { Visible::Yes => false, Visible::Editable => true, Visible::No => return, }; + let doc_aliases = ctx.doc_aliases(&func); self.add( render_fn( - RenderContext::new(ctx).private_editable(is_private_editable), + RenderContext::new(ctx) + .private_editable(is_private_editable) + .doc_aliases(doc_aliases), path_ctx, local_name, func, ) - .build(), + .build(ctx.db), ); } @@ -299,20 +317,26 @@ impl Completions { receiver: Option<hir::Name>, local_name: Option<hir::Name>, ) { + if !ctx.check_stability(Some(&func.attrs(ctx.db))) { + return; + } let is_private_editable = match ctx.is_visible(&func) { Visible::Yes => false, Visible::Editable => true, Visible::No => return, }; + let doc_aliases = ctx.doc_aliases(&func); self.add( render_method( - RenderContext::new(ctx).private_editable(is_private_editable), + RenderContext::new(ctx) + .private_editable(is_private_editable) + .doc_aliases(doc_aliases), dot_access, receiver, local_name, func, ) - .build(), + .build(ctx.db), ); } @@ -323,26 +347,34 @@ impl Completions { func: hir::Function, import: LocatedImport, ) { + if !ctx.check_stability(Some(&func.attrs(ctx.db))) { + return; + } let is_private_editable = match ctx.is_visible(&func) { Visible::Yes => false, Visible::Editable => true, Visible::No => return, }; + let doc_aliases = ctx.doc_aliases(&func); self.add( render_method( RenderContext::new(ctx) .private_editable(is_private_editable) + .doc_aliases(doc_aliases) .import_to_add(Some(import)), dot_access, None, None, func, ) - .build(), + .build(ctx.db), ); } pub(crate) fn add_const(&mut self, ctx: &CompletionContext<'_>, konst: hir::Const) { + if !ctx.check_stability(Some(&konst.attrs(ctx.db))) { + return; + } let is_private_editable = match ctx.is_visible(&konst) { Visible::Yes => false, Visible::Editable => true, @@ -359,6 +391,9 @@ impl Completions { ctx: &CompletionContext<'_>, type_alias: hir::TypeAlias, ) { + if !ctx.check_stability(Some(&type_alias.attrs(ctx.db))) { + return; + } let is_private_editable = match ctx.is_visible(&type_alias) { Visible::Yes => false, Visible::Editable => true, @@ -375,6 +410,9 @@ impl Completions { ctx: &CompletionContext<'_>, type_alias: hir::TypeAlias, ) { + if !ctx.check_stability(Some(&type_alias.attrs(ctx.db))) { + return; + } self.add_opt(render_type_alias_with_eq(RenderContext::new(ctx), type_alias)); } @@ -385,10 +423,13 @@ impl Completions { variant: hir::Variant, path: hir::ModPath, ) { + if !ctx.check_stability(Some(&variant.attrs(ctx.db))) { + return; + } if let Some(builder) = render_variant_lit(RenderContext::new(ctx), path_ctx, None, variant, Some(path)) { - self.add(builder.build()); + self.add(builder.build(ctx.db)); } } @@ -399,6 +440,9 @@ impl Completions { variant: hir::Variant, local_name: Option<hir::Name>, ) { + if !ctx.check_stability(Some(&variant.attrs(ctx.db))) { + return; + } if let PathCompletionCtx { kind: PathKind::Pat { pat_ctx }, .. } = path_ctx { cov_mark::hit!(enum_variant_pattern_path); self.add_variant_pat(ctx, pat_ctx, Some(path_ctx), variant, local_name); @@ -408,7 +452,7 @@ impl Completions { if let Some(builder) = render_variant_lit(RenderContext::new(ctx), path_ctx, local_name, variant, None) { - self.add(builder.build()); + self.add(builder.build(ctx.db)); } } @@ -420,13 +464,17 @@ impl Completions { field: hir::Field, ty: &hir::Type, ) { + if !ctx.check_stability(Some(&field.attrs(ctx.db))) { + return; + } let is_private_editable = match ctx.is_visible(&field) { Visible::Yes => false, Visible::Editable => true, Visible::No => return, }; + let doc_aliases = ctx.doc_aliases(&field); let item = render_field( - RenderContext::new(ctx).private_editable(is_private_editable), + RenderContext::new(ctx).private_editable(is_private_editable).doc_aliases(doc_aliases), dot_access, receiver, field, @@ -443,10 +491,13 @@ impl Completions { path: Option<hir::ModPath>, local_name: Option<hir::Name>, ) { + if !ctx.check_stability(Some(&strukt.attrs(ctx.db))) { + return; + } if let Some(builder) = render_struct_literal(RenderContext::new(ctx), path_ctx, strukt, path, local_name) { - self.add(builder.build()); + self.add(builder.build(ctx.db)); } } @@ -457,6 +508,9 @@ impl Completions { path: Option<hir::ModPath>, local_name: Option<hir::Name>, ) { + if !ctx.check_stability(Some(&un.attrs(ctx.db))) { + return; + } let item = render_union_literal(RenderContext::new(ctx), un, path, local_name); self.add_opt(item); } @@ -468,17 +522,20 @@ impl Completions { field: usize, ty: &hir::Type, ) { + // Only used for (unnamed) tuples, whose all fields *are* stable. No need to check + // stability here. let item = render_tuple_field(RenderContext::new(ctx), receiver, field, ty); self.add(item); } pub(crate) fn add_lifetime(&mut self, ctx: &CompletionContext<'_>, name: hir::Name) { CompletionItem::new(SymbolKind::LifetimeParam, ctx.source_range(), name.to_smol_str()) - .add_to(self) + .add_to(self, ctx.db) } pub(crate) fn add_label(&mut self, ctx: &CompletionContext<'_>, name: hir::Name) { - CompletionItem::new(SymbolKind::Label, ctx.source_range(), name.to_smol_str()).add_to(self) + CompletionItem::new(SymbolKind::Label, ctx.source_range(), name.to_smol_str()) + .add_to(self, ctx.db) } pub(crate) fn add_variant_pat( @@ -489,6 +546,9 @@ impl Completions { variant: hir::Variant, local_name: Option<hir::Name>, ) { + if !ctx.check_stability(Some(&variant.attrs(ctx.db))) { + return; + } self.add_opt(render_variant_pat( RenderContext::new(ctx), pattern_ctx, @@ -506,6 +566,9 @@ impl Completions { variant: hir::Variant, path: hir::ModPath, ) { + if !ctx.check_stability(Some(&variant.attrs(ctx.db))) { + return; + } let path = Some(&path); self.add_opt(render_variant_pat( RenderContext::new(ctx), @@ -524,6 +587,9 @@ impl Completions { strukt: hir::Struct, local_name: Option<hir::Name>, ) { + if !ctx.check_stability(Some(&strukt.attrs(ctx.db))) { + return; + } self.add_opt(render_struct_pat(RenderContext::new(ctx), pattern_ctx, strukt, local_name)); } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs index bb950c76f..466f0b1fb 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs @@ -93,7 +93,7 @@ pub(crate) fn complete_attribute_path( acc.add_macro(ctx, path_ctx, m, name) } hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => { - acc.add_module(ctx, path_ctx, m, name) + acc.add_module(ctx, path_ctx, m, name, vec![]) } _ => (), } @@ -104,12 +104,12 @@ pub(crate) fn complete_attribute_path( Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx), // only show modules in a fresh UseTree Qualified::No => { - ctx.process_all_names(&mut |name, def| match def { + ctx.process_all_names(&mut |name, def, doc_aliases| match def { hir::ScopeDef::ModuleDef(hir::ModuleDef::Macro(m)) if m.is_attr(ctx.db) => { acc.add_macro(ctx, path_ctx, m, name) } hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => { - acc.add_module(ctx, path_ctx, m, name) + acc.add_module(ctx, path_ctx, m, name, doc_aliases) } _ => (), }); @@ -139,7 +139,7 @@ pub(crate) fn complete_attribute_path( } if is_inner || !attr_completion.prefer_inner { - item.add_to(acc); + item.add_to(acc, ctx.db); } }; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs index 7ef4ff30b..19bfd294b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs @@ -12,7 +12,7 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) { let add_completion = |item: &str| { let mut completion = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), item); completion.insert_text(format!(r#""{item}""#)); - acc.add(completion.build()); + acc.add(completion.build(ctx.db)); }; let previous = iter::successors(ctx.original_token.prev_token(), |t| { @@ -33,11 +33,11 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) { let mut item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s); item.insert_text(insert_text); - acc.add(item.build()); + acc.add(item.build(ctx.db)); }), None => ctx.krate.potential_cfg(ctx.db).get_cfg_keys().cloned().unique().for_each(|s| { let item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s); - acc.add(item.build()); + acc.add(item.build(ctx.db)); }), }; } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs index 793c22630..9447bc7db 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs @@ -34,7 +34,7 @@ pub(crate) fn complete_derive_path( acc.add_macro(ctx, path_ctx, mac, name) } ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => { - acc.add_module(ctx, path_ctx, m, name) + acc.add_module(ctx, path_ctx, m, name, vec![]) } _ => (), } @@ -43,7 +43,7 @@ pub(crate) fn complete_derive_path( Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx), // only show modules in a fresh UseTree Qualified::No => { - ctx.process_all_names(&mut |name, def| { + ctx.process_all_names(&mut |name, def, doc_aliases| { let mac = match def { ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) if !existing_derives.contains(&mac) && mac.is_derive(ctx.db) => @@ -51,7 +51,7 @@ pub(crate) fn complete_derive_path( mac } ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => { - return acc.add_module(ctx, path_ctx, m, name); + return acc.add_module(ctx, path_ctx, m, name, doc_aliases); } _ => return, }; @@ -90,7 +90,7 @@ pub(crate) fn complete_derive_path( item.documentation(docs); } item.lookup_by(lookup); - item.add_to(acc); + item.add_to(acc, ctx.db); } None => acc.add_macro(ctx, path_ctx, mac, name), } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs index 818c3cfd5..6bc6f34ed 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs @@ -56,6 +56,6 @@ pub(super) fn complete_lint( }; let mut item = CompletionItem::new(SymbolKind::Attribute, ctx.source_range(), label); item.documentation(hir::Documentation::new(description.to_owned())); - item.add_to(acc) + item.add_to(acc, ctx.db) } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs index a29417133..14f464b77 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs @@ -37,7 +37,7 @@ pub(super) fn complete_repr( if let Some((snippet, cap)) = snippet.zip(ctx.config.snippet_cap) { item.insert_snippet(cap, snippet); } - item.add_to(acc); + item.add_to(acc, ctx.db); } } } 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 77246379e..c5bbb7f8d 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 @@ -23,7 +23,7 @@ pub(crate) fn complete_dot( let mut item = CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), "await"); item.detail("expr.await"); - item.add_to(acc); + item.add_to(acc, ctx.db); } if let DotAccessKind::Method { .. } = dot_access.kind { @@ -105,13 +105,20 @@ fn complete_fields( mut named_field: impl FnMut(&mut Completions, hir::Field, hir::Type), mut tuple_index: impl FnMut(&mut Completions, usize, hir::Type), ) { + let mut seen_names = FxHashSet::default(); for receiver in receiver.autoderef(ctx.db) { for (field, ty) in receiver.fields(ctx.db) { - named_field(acc, field, ty); + if seen_names.insert(field.name(ctx.db)) { + named_field(acc, field, ty); + } } for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() { - // Tuple fields are always public (tuple struct fields are handled above). - tuple_index(acc, i, ty); + // Tuples are always the last type in a deref chain, so just check if the name is + // already seen without inserting into the hashset. + if !seen_names.contains(&hir::Name::new_tuple_field(i)) { + // Tuple fields are always public (tuple struct fields are handled above). + tuple_index(acc, i, ty); + } } } } @@ -173,6 +180,43 @@ fn foo(s: S) { s.$0 } } #[test] + fn no_unstable_method_on_stable() { + check( + r#" +//- /main.rs crate:main deps:std +fn foo(s: std::S) { s.$0 } +//- /std.rs crate:std +pub struct S; +impl S { + #[unstable] + pub fn bar(&self) {} +} +"#, + expect![""], + ); + } + + #[test] + fn unstable_method_on_nightly() { + check( + r#" +//- toolchain:nightly +//- /main.rs crate:main deps:std +fn foo(s: std::S) { s.$0 } +//- /std.rs crate:std +pub struct S; +impl S { + #[unstable] + pub fn bar(&self) {} +} +"#, + expect![[r#" + me bar() fn(&self) + "#]], + ); + } + + #[test] fn test_struct_field_completion_self() { check( r#" @@ -635,6 +679,74 @@ impl T { } #[test] + fn test_field_no_same_name() { + check( + r#" +//- minicore: deref +struct A { field: u8 } +struct B { field: u16, another: u32 } +impl core::ops::Deref for A { + type Target = B; + fn deref(&self) -> &Self::Target { loop {} } +} +fn test(a: A) { + a.$0 +} +"#, + expect![[r#" + fd another u32 + fd field u8 + me deref() (use core::ops::Deref) fn(&self) -> &<Self as Deref>::Target + "#]], + ); + } + + #[test] + fn test_tuple_field_no_same_index() { + check( + r#" +//- minicore: deref +struct A(u8); +struct B(u16, u32); +impl core::ops::Deref for A { + type Target = B; + fn deref(&self) -> &Self::Target { loop {} } +} +fn test(a: A) { + a.$0 +} +"#, + expect![[r#" + fd 0 u8 + fd 1 u32 + me deref() (use core::ops::Deref) fn(&self) -> &<Self as Deref>::Target + "#]], + ); + } + + #[test] + fn test_tuple_struct_deref_to_tuple_no_same_index() { + check( + r#" +//- minicore: deref +struct A(u8); +impl core::ops::Deref for A { + type Target = (u16, u32); + fn deref(&self) -> &Self::Target { loop {} } +} +fn test(a: A) { + a.$0 +} +"#, + expect![[r#" + fd 0 u8 + fd 1 u32 + me deref() (use core::ops::Deref) fn(&self) -> &<Self as Deref>::Target + "#]], + ); + } + + #[test] fn test_completion_works_in_consts() { check( r#" @@ -942,4 +1054,45 @@ fn test(thing: impl Encrypt) { "#]], ) } + + #[test] + fn only_consider_same_type_once() { + check( + r#" +//- minicore: deref +struct A(u8); +struct B(u16); +impl core::ops::Deref for A { + type Target = B; + fn deref(&self) -> &Self::Target { loop {} } +} +impl core::ops::Deref for B { + type Target = A; + fn deref(&self) -> &Self::Target { loop {} } +} +fn test(a: A) { + a.$0 +} +"#, + expect![[r#" + fd 0 u8 + me deref() (use core::ops::Deref) fn(&self) -> &<Self as Deref>::Target + "#]], + ); + } + + #[test] + fn no_inference_var_in_completion() { + check( + r#" +struct S<T>(T); +fn test(s: S<Unknown>) { + s.$0 +} +"#, + expect![[r#" + fd 0 {unknown} + "#]], + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs index 1002be211..419b86456 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs @@ -37,10 +37,10 @@ pub(crate) fn complete_cargo_env_vars( guard_env_macro(expanded, &ctx.sema)?; let range = expanded.text_range_between_quotes()?; - CARGO_DEFINED_VARS.iter().for_each(|(var, detail)| { + CARGO_DEFINED_VARS.into_iter().for_each(|&(var, detail)| { let mut item = CompletionItem::new(CompletionItemKind::Keyword, range, var); - item.detail(*detail); - item.add_to(acc); + item.detail(detail); + item.add_to(acc, ctx.db); }); Some(()) 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 cfe4787f7..9daa6984c 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 @@ -88,7 +88,13 @@ pub(crate) fn complete_expr_path( let module_scope = module.scope(ctx.db, Some(ctx.module)); for (name, def) in module_scope { if scope_def_applicable(def) { - acc.add_path_resolution(ctx, path_ctx, name, def); + acc.add_path_resolution( + ctx, + path_ctx, + name, + def, + ctx.doc_aliases_in_scope(def), + ); } } } @@ -212,7 +218,7 @@ pub(crate) fn complete_expr_path( } } } - ctx.process_all_names(&mut |name, def| match def { + ctx.process_all_names(&mut |name, def, doc_aliases| match def { ScopeDef::ModuleDef(hir::ModuleDef::Trait(t)) => { let assocs = t.items_with_supertraits(ctx.db); match &*assocs { @@ -220,12 +226,14 @@ pub(crate) fn complete_expr_path( // there is no associated item path that can be constructed with them [] => (), // FIXME: Render the assoc item with the trait qualified - &[_item] => acc.add_path_resolution(ctx, path_ctx, name, def), + &[_item] => acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases), // FIXME: Append `::` to the thing here, since a trait on its own won't work - [..] => acc.add_path_resolution(ctx, path_ctx, name, def), + [..] => acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases), } } - _ if scope_def_applicable(def) => acc.add_path_resolution(ctx, path_ctx, name, def), + _ if scope_def_applicable(def) => { + acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases) + } _ => (), }); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs index 4e89ef696..c717a9cb5 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs @@ -42,7 +42,7 @@ const SUPPORTED_CALLING_CONVENTIONS: &[&str] = &[ pub(crate) fn complete_extern_abi( acc: &mut Completions, - _ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_>, expanded: &ast::String, ) -> Option<()> { if !expanded.syntax().parent().map_or(false, |it| ast::Abi::can_cast(it.kind())) { @@ -51,7 +51,7 @@ pub(crate) fn complete_extern_abi( let abi_str = expanded; let source_range = abi_str.text_range_between_quotes()?; for &abi in SUPPORTED_CALLING_CONVENTIONS { - CompletionItem::new(CompletionItemKind::Keyword, source_range, abi).add_to(acc); + CompletionItem::new(CompletionItemKind::Keyword, source_range, abi).add_to(acc, ctx.db); } Some(()) } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs index 0979f6a6d..39c1b7f7b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs @@ -257,30 +257,24 @@ fn import_on_the_fly( }; let user_input_lowercased = potential_import_name.to_lowercase(); - acc.add_all( - import_assets - .search_for_imports( - &ctx.sema, - ctx.config.insert_use.prefix_kind, - ctx.config.prefer_no_std, - ) - .into_iter() - .filter(ns_filter) - .filter(|import| { - !ctx.is_item_hidden(&import.item_to_import) - && !ctx.is_item_hidden(&import.original_item) - }) - .sorted_by_key(|located_import| { - compute_fuzzy_completion_order_key( - &located_import.import_path, - &user_input_lowercased, - ) - }) - .filter_map(|import| { - render_resolution_with_import(RenderContext::new(ctx), path_ctx, import) - }) - .map(|builder| builder.build()), - ); + import_assets + .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std) + .into_iter() + .filter(ns_filter) + .filter(|import| { + let original_item = &import.original_item; + !ctx.is_item_hidden(&import.item_to_import) + && !ctx.is_item_hidden(original_item) + && ctx.check_stability(original_item.attrs(ctx.db).as_deref()) + }) + .sorted_by_key(|located_import| { + compute_fuzzy_completion_order_key(&located_import.import_path, &user_input_lowercased) + }) + .filter_map(|import| { + render_resolution_with_import(RenderContext::new(ctx), path_ctx, import) + }) + .map(|builder| builder.build(ctx.db)) + .for_each(|item| acc.add(item)); Some(()) } @@ -305,30 +299,24 @@ fn import_on_the_fly_pat_( }; let user_input_lowercased = potential_import_name.to_lowercase(); - acc.add_all( - import_assets - .search_for_imports( - &ctx.sema, - ctx.config.insert_use.prefix_kind, - ctx.config.prefer_no_std, - ) - .into_iter() - .filter(ns_filter) - .filter(|import| { - !ctx.is_item_hidden(&import.item_to_import) - && !ctx.is_item_hidden(&import.original_item) - }) - .sorted_by_key(|located_import| { - compute_fuzzy_completion_order_key( - &located_import.import_path, - &user_input_lowercased, - ) - }) - .filter_map(|import| { - render_resolution_with_import_pat(RenderContext::new(ctx), pattern_ctx, import) - }) - .map(|builder| builder.build()), - ); + import_assets + .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std) + .into_iter() + .filter(ns_filter) + .filter(|import| { + let original_item = &import.original_item; + !ctx.is_item_hidden(&import.item_to_import) + && !ctx.is_item_hidden(original_item) + && ctx.check_stability(original_item.attrs(ctx.db).as_deref()) + }) + .sorted_by_key(|located_import| { + compute_fuzzy_completion_order_key(&located_import.import_path, &user_input_lowercased) + }) + .filter_map(|import| { + render_resolution_with_import_pat(RenderContext::new(ctx), pattern_ctx, import) + }) + .map(|builder| builder.build(ctx.db)) + .for_each(|item| acc.add(item)); Some(()) } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs index d8b8a190e..8b38d4f01 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs @@ -40,7 +40,7 @@ pub(crate) fn complete_fn_param( }; // Completion lookup is omitted intentionally here. // See the full discussion: https://github.com/rust-lang/rust-analyzer/issues/12073 - item.add_to(acc) + item.add_to(acc, ctx.db) }; match kind { @@ -50,7 +50,7 @@ pub(crate) fn complete_fn_param( ParamKind::Closure(closure) => { let stmt_list = closure.syntax().ancestors().find_map(ast::StmtList::cast)?; params_from_stmt_list_scope(ctx, stmt_list, |name, ty| { - add_new_item_to_acc(&format!("{name}: {ty}")); + add_new_item_to_acc(&format!("{}: {ty}", name.display(ctx.db))); }); } } @@ -100,7 +100,9 @@ fn fill_fn_params( if let Some(stmt_list) = function.syntax().parent().and_then(ast::StmtList::cast) { params_from_stmt_list_scope(ctx, stmt_list, |name, ty| { - file_params.entry(format!("{name}: {ty}")).or_insert(name.to_string()); + file_params + .entry(format!("{}: {ty}", name.display(ctx.db))) + .or_insert(name.display(ctx.db).to_string()); }); } remove_duplicated(&mut file_params, param_list.params()); @@ -127,7 +129,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/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs index 5c46c5806..8e904fd60 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs @@ -32,7 +32,7 @@ pub(crate) fn format_string( let source_range = TextRange::new(brace_offset, cursor); ctx.locals.iter().for_each(|(name, _)| { CompletionItem::new(CompletionItemKind::Binding, source_range, name.to_smol_str()) - .add_to(acc); + .add_to(acc, ctx.db); }) } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs index 60d05ae46..5ea6a49b1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs @@ -45,7 +45,7 @@ pub(crate) fn complete_item_list( acc.add_macro(ctx, path_ctx, m, name) } hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => { - acc.add_module(ctx, path_ctx, m, name) + acc.add_module(ctx, path_ctx, m, name, vec![]) } _ => (), } @@ -55,12 +55,12 @@ pub(crate) fn complete_item_list( } Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx), Qualified::No if ctx.qualifier_ctx.none() => { - ctx.process_all_names(&mut |name, def| match def { + ctx.process_all_names(&mut |name, def, doc_aliases| match def { hir::ScopeDef::ModuleDef(hir::ModuleDef::Macro(m)) if m.is_fn_like(ctx.db) => { acc.add_macro(ctx, path_ctx, m, name) } hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => { - acc.add_module(ctx, path_ctx, m, name) + acc.add_module(ctx, path_ctx, m, name, doc_aliases) } _ => (), }); 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 889d90095..269e40e6e 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 @@ -150,21 +150,24 @@ fn complete_trait_impl( impl_def: &ast::Impl, ) { if let Some(hir_impl) = ctx.sema.to_def(impl_def) { - get_missing_assoc_items(&ctx.sema, impl_def).into_iter().for_each(|item| { - use self::ImplCompletionKind::*; - match (item, kind) { - (hir::AssocItem::Function(func), All | Fn) => { - add_function_impl(acc, ctx, replacement_range, func, hir_impl) + get_missing_assoc_items(&ctx.sema, impl_def) + .into_iter() + .filter(|item| ctx.check_stability(Some(&item.attrs(ctx.db)))) + .for_each(|item| { + use self::ImplCompletionKind::*; + match (item, kind) { + (hir::AssocItem::Function(func), All | Fn) => { + add_function_impl(acc, ctx, replacement_range, func, hir_impl) + } + (hir::AssocItem::TypeAlias(type_alias), All | TypeAlias) => { + add_type_alias_impl(acc, ctx, replacement_range, type_alias, hir_impl) + } + (hir::AssocItem::Const(const_), All | Const) => { + add_const_impl(acc, ctx, replacement_range, const_, hir_impl) + } + _ => {} } - (hir::AssocItem::TypeAlias(type_alias), All | TypeAlias) => { - add_type_alias_impl(acc, ctx, replacement_range, type_alias, hir_impl) - } - (hir::AssocItem::Const(const_), All | Const) => { - add_const_impl(acc, ctx, replacement_range, const_, hir_impl) - } - _ => {} - } - }); + }); } } @@ -179,7 +182,7 @@ fn add_function_impl( let label = format!( "fn {}({})", - fn_name, + fn_name.display(ctx.db), if func.assoc_fn_params(ctx.db).is_empty() { "" } else { ".." } ); @@ -190,7 +193,7 @@ fn add_function_impl( }; let mut item = CompletionItem::new(completion_kind, replacement_range, label); - item.lookup_by(format!("fn {fn_name}")) + item.lookup_by(format!("fn {}", fn_name.display(ctx.db))) .set_documentation(func.docs(ctx.db)) .set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() }); @@ -213,7 +216,7 @@ fn add_function_impl( item.text_edit(TextEdit::replace(replacement_range, header)); } }; - item.add_to(acc); + item.add_to(acc, ctx.db); } } } @@ -224,9 +227,8 @@ fn get_transformed_assoc_item( assoc_item: ast::AssocItem, impl_def: hir::Impl, ) -> Option<ast::AssocItem> { - let assoc_item = assoc_item.clone_for_update(); let trait_ = impl_def.trait_(ctx.db)?; - let source_scope = &ctx.sema.scope_for_def(trait_); + let source_scope = &ctx.sema.scope(assoc_item.syntax())?; let target_scope = &ctx.sema.scope(ctx.sema.source(impl_def)?.syntax().value)?; let transform = PathTransform::trait_impl( target_scope, @@ -235,6 +237,9 @@ fn get_transformed_assoc_item( ctx.sema.source(impl_def)?.value, ); + let assoc_item = assoc_item.clone_for_update(); + // FIXME: Paths in nested macros are not handled well. See + // `macro_generated_assoc_item2` test. transform.apply(assoc_item.syntax()); assoc_item.remove_attrs_and_docs(); Some(assoc_item) @@ -297,7 +302,7 @@ fn add_type_alias_impl( item.text_edit(TextEdit::replace(replacement_range, decl)); } }; - item.add_to(acc); + item.add_to(acc, ctx.db); } } } @@ -337,7 +342,7 @@ fn add_const_impl( ), None => item.text_edit(TextEdit::replace(replacement_range, replacement)), }; - item.add_to(acc); + item.add_to(acc, ctx.db); } } } @@ -831,6 +836,33 @@ impl Test for () { } #[test] + fn fn_with_lifetimes() { + check_edit( + "fn foo", + r#" +trait Test<'a, 'b, T> { + fn foo(&self, a: &'a T, b: &'b T) -> &'a T; +} + +impl<'x, 'y, A> Test<'x, 'y, A> for () { + t$0 +} +"#, + r#" +trait Test<'a, 'b, T> { + fn foo(&self, a: &'a T, b: &'b T) -> &'a T; +} + +impl<'x, 'y, A> Test<'x, 'y, A> for () { + fn foo(&self, a: &'x A, b: &'y A) -> &'x A { + $0 +} +} +"#, + ); + } + + #[test] fn complete_without_name() { let test = |completion: &str, hint: &str, completed: &str, next_sibling: &str| { check_edit( @@ -1191,6 +1223,81 @@ impl Foo for Test { } #[test] + fn macro_generated_assoc_item() { + check_edit( + "fn method", + r#" +macro_rules! ty { () => { i32 } } +trait SomeTrait { type Output; } +impl SomeTrait for i32 { type Output = i64; } +macro_rules! define_method { + () => { + fn method(&mut self, params: <ty!() as SomeTrait>::Output); + }; +} +trait AnotherTrait { define_method!(); } +impl AnotherTrait for () { + $0 +} +"#, + r#" +macro_rules! ty { () => { i32 } } +trait SomeTrait { type Output; } +impl SomeTrait for i32 { type Output = i64; } +macro_rules! define_method { + () => { + fn method(&mut self, params: <ty!() as SomeTrait>::Output); + }; +} +trait AnotherTrait { define_method!(); } +impl AnotherTrait for () { + fn method(&mut self,params: <ty!()as SomeTrait>::Output) { + $0 +} +} +"#, + ); + } + + // FIXME: `T` in `ty!(T)` should be replaced by `PathTransform`. + #[test] + fn macro_generated_assoc_item2() { + check_edit( + "fn method", + r#" +macro_rules! ty { ($me:ty) => { $me } } +trait SomeTrait { type Output; } +impl SomeTrait for i32 { type Output = i64; } +macro_rules! define_method { + ($t:ty) => { + fn method(&mut self, params: <ty!($t) as SomeTrait>::Output); + }; +} +trait AnotherTrait<T: SomeTrait> { define_method!(T); } +impl AnotherTrait<i32> for () { + $0 +} +"#, + r#" +macro_rules! ty { ($me:ty) => { $me } } +trait SomeTrait { type Output; } +impl SomeTrait for i32 { type Output = i64; } +macro_rules! define_method { + ($t:ty) => { + fn method(&mut self, params: <ty!($t) as SomeTrait>::Output); + }; +} +trait AnotherTrait<T: SomeTrait> { define_method!(T); } +impl AnotherTrait<i32> for () { + fn method(&mut self,params: <ty!(T)as SomeTrait>::Output) { + $0 +} +} +"#, + ); + } + + #[test] fn includes_gat_generics() { check_edit( "type Ty", diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs index 3b79def63..2c6cbf614 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs @@ -329,6 +329,7 @@ fn foo() { fn complete_label_in_for_iterable() { check( r#" +//- minicore: iterator fn foo() { 'outer: for _ in [{ 'inner: loop { break '$0 } }] {} } 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 950731eb4..d3e75c6da 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 @@ -52,7 +52,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_map(|module| Some(module.name(ctx.db)?.display(ctx.db).to_string())) .filter(|module| module != ctx.original_token.text()) .collect::<FxHashSet<_>>(); @@ -99,7 +99,7 @@ pub(crate) fn complete_mod( label.push(';'); } let item = CompletionItem::new(SymbolKind::Module, ctx.source_range(), &label); - item.add_to(acc) + item.add_to(acc, ctx.db) }); Some(()) 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 58d5bf114..40b2c831a 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 @@ -64,7 +64,7 @@ pub(crate) fn complete_pattern( // FIXME: ideally, we should look at the type we are matching against and // suggest variants + auto-imports - ctx.process_all_names(&mut |name, res| { + ctx.process_all_names(&mut |name, res, _| { let add_simple_path = match res { hir::ScopeDef::ModuleDef(def) => match def { hir::ModuleDef::Adt(hir::Adt::Struct(strukt)) => { @@ -127,7 +127,7 @@ pub(crate) fn complete_pattern_path( }; if add_resolution { - acc.add_path_resolution(ctx, path_ctx, name, def); + acc.add_path_resolution(ctx, path_ctx, name, def, vec![]); } } } @@ -164,7 +164,7 @@ pub(crate) fn complete_pattern_path( Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx), Qualified::No => { // this will only be hit if there are brackets or braces, otherwise this will be parsed as an ident pattern - ctx.process_all_names(&mut |name, res| { + ctx.process_all_names(&mut |name, res, doc_aliases| { // FIXME: we should check what kind of pattern we are in and filter accordingly let add_completion = match res { ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => mac.is_fn_like(ctx.db), @@ -175,7 +175,7 @@ pub(crate) fn complete_pattern_path( _ => false, }; if add_completion { - acc.add_path_resolution(ctx, path_ctx, name, res); + acc.add_path_resolution(ctx, path_ctx, name, res, doc_aliases); } }); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs index c55bd9aaa..2ffe12337 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs @@ -64,7 +64,7 @@ pub(crate) fn complete_postfix( &format!("drop($0{receiver_text})"), ); item.set_documentation(drop_fn.docs(ctx.db)); - item.add_to(acc); + item.add_to(acc, ctx.db); } } } @@ -78,14 +78,14 @@ pub(crate) fn complete_postfix( "if let Ok {}", &format!("if let Ok($1) = {receiver_text} {{\n $0\n}}"), ) - .add_to(acc); + .add_to(acc, ctx.db); postfix_snippet( "while", "while let Ok {}", &format!("while let Ok($1) = {receiver_text} {{\n $0\n}}"), ) - .add_to(acc); + .add_to(acc, ctx.db); } TryEnum::Option => { postfix_snippet( @@ -93,22 +93,22 @@ pub(crate) fn complete_postfix( "if let Some {}", &format!("if let Some($1) = {receiver_text} {{\n $0\n}}"), ) - .add_to(acc); + .add_to(acc, ctx.db); postfix_snippet( "while", "while let Some {}", &format!("while let Some($1) = {receiver_text} {{\n $0\n}}"), ) - .add_to(acc); + .add_to(acc, ctx.db); } } } else if receiver_ty.is_bool() || receiver_ty.is_unknown() { postfix_snippet("if", "if expr {}", &format!("if {receiver_text} {{\n $0\n}}")) - .add_to(acc); + .add_to(acc, ctx.db); postfix_snippet("while", "while expr {}", &format!("while {receiver_text} {{\n $0\n}}")) - .add_to(acc); - postfix_snippet("not", "!expr", &format!("!{receiver_text}")).add_to(acc); + .add_to(acc, ctx.db); + postfix_snippet("not", "!expr", &format!("!{receiver_text}")).add_to(acc, ctx.db); } else if let Some(trait_) = ctx.famous_defs().core_iter_IntoIterator() { if receiver_ty.impls_trait(ctx.db, trait_, &[]) { postfix_snippet( @@ -116,12 +116,12 @@ pub(crate) fn complete_postfix( "for ele in expr {}", &format!("for ele in {receiver_text} {{\n $0\n}}"), ) - .add_to(acc); + .add_to(acc, ctx.db); } } - postfix_snippet("ref", "&expr", &format!("&{receiver_text}")).add_to(acc); - postfix_snippet("refm", "&mut expr", &format!("&mut {receiver_text}")).add_to(acc); + postfix_snippet("ref", "&expr", &format!("&{receiver_text}")).add_to(acc, ctx.db); + postfix_snippet("refm", "&mut expr", &format!("&mut {receiver_text}")).add_to(acc, ctx.db); let mut unsafe_should_be_wrapped = true; if dot_receiver.syntax().kind() == BLOCK_EXPR { @@ -137,7 +137,7 @@ pub(crate) fn complete_postfix( } else { format!("unsafe {receiver_text}") }; - postfix_snippet("unsafe", "unsafe {}", &unsafe_completion_string).add_to(acc); + postfix_snippet("unsafe", "unsafe {}", &unsafe_completion_string).add_to(acc, ctx.db); // The rest of the postfix completions create an expression that moves an argument, // so it's better to consider references now to avoid breaking the compilation @@ -162,7 +162,7 @@ pub(crate) fn complete_postfix( "match expr {}", &format!("match {receiver_text} {{\n Ok(${{1:_}}) => {{$2}},\n Err(${{3:_}}) => {{$0}},\n}}"), ) - .add_to(acc); + .add_to(acc, ctx.db); } TryEnum::Option => { postfix_snippet( @@ -172,7 +172,7 @@ pub(crate) fn complete_postfix( "match {receiver_text} {{\n Some(${{1:_}}) => {{$2}},\n None => {{$0}},\n}}" ), ) - .add_to(acc); + .add_to(acc, ctx.db); } }, None => { @@ -181,20 +181,23 @@ pub(crate) fn complete_postfix( "match expr {}", &format!("match {receiver_text} {{\n ${{1:_}} => {{$0}},\n}}"), ) - .add_to(acc); + .add_to(acc, ctx.db); } } - postfix_snippet("box", "Box::new(expr)", &format!("Box::new({receiver_text})")).add_to(acc); - postfix_snippet("dbg", "dbg!(expr)", &format!("dbg!({receiver_text})")).add_to(acc); // fixme - postfix_snippet("dbgr", "dbg!(&expr)", &format!("dbg!(&{receiver_text})")).add_to(acc); - postfix_snippet("call", "function(expr)", &format!("${{1}}({receiver_text})")).add_to(acc); + postfix_snippet("box", "Box::new(expr)", &format!("Box::new({receiver_text})")) + .add_to(acc, ctx.db); + postfix_snippet("dbg", "dbg!(expr)", &format!("dbg!({receiver_text})")).add_to(acc, ctx.db); // fixme + postfix_snippet("dbgr", "dbg!(&expr)", &format!("dbg!(&{receiver_text})")).add_to(acc, ctx.db); + postfix_snippet("call", "function(expr)", &format!("${{1}}({receiver_text})")) + .add_to(acc, ctx.db); if let Some(parent) = dot_receiver.syntax().parent().and_then(|p| p.parent()) { if matches!(parent.kind(), STMT_LIST | EXPR_STMT) { - postfix_snippet("let", "let", &format!("let $0 = {receiver_text};")).add_to(acc); + postfix_snippet("let", "let", &format!("let $0 = {receiver_text};")) + .add_to(acc, ctx.db); postfix_snippet("letm", "let mut", &format!("let mut $0 = {receiver_text};")) - .add_to(acc); + .add_to(acc, ctx.db); } } @@ -315,7 +318,7 @@ fn add_custom_postfix_completions( for import in imports.into_iter() { builder.add_import(import); } - builder.add_to(acc); + builder.add_to(acc, ctx.db); }, ); None 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 dfcc78e92..cb242e4aa 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 @@ -60,7 +60,7 @@ pub(crate) fn add_format_like_completions( format!(r#"{}({}, {})"#, macro_name, out, exprs.join(", ")) }; - postfix_snippet(label, macro_name, &snippet).add_to(acc); + postfix_snippet(label, macro_name, &snippet).add_to(acc, ctx.db); } } } 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 0521e735d..945c3945b 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 @@ -69,7 +69,7 @@ pub(crate) fn complete_record_expr_fields( let mut item = CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), ".."); item.insert_text("."); - item.add_to(acc); + item.add_to(acc, ctx.db); return; } missing_fields @@ -98,7 +98,7 @@ pub(crate) fn add_default_update( postfix_match: Some(CompletionRelevancePostfixMatch::Exact), ..Default::default() }); - item.add_to(acc); + item.add_to(acc, ctx.db); } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs index da1f0542d..e9831a5b2 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs @@ -32,8 +32,8 @@ pub(crate) fn complete_expr_snippet( } if in_block_expr { - snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc); - snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc); + snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc, ctx.db); + snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc, ctx.db); let item = snippet( ctx, cap, @@ -45,7 +45,7 @@ macro_rules! $1 { }; }", ); - item.add_to(acc); + item.add_to(acc, ctx.db); } } @@ -88,7 +88,7 @@ mod tests { }", ); item.lookup_by("tmod"); - item.add_to(acc); + item.add_to(acc, ctx.db); let mut item = snippet( ctx, @@ -101,7 +101,7 @@ fn ${1:feature}() { }", ); item.lookup_by("tfn"); - item.add_to(acc); + item.add_to(acc, ctx.db); let item = snippet( ctx, @@ -114,7 +114,7 @@ macro_rules! $1 { }; }", ); - item.add_to(acc); + item.add_to(acc, ctx.db); } } @@ -146,7 +146,7 @@ fn add_custom_completions( builder.add_import(import); } builder.set_detail(snip.description.clone()); - builder.add_to(acc); + builder.add_to(acc, ctx.db); }, ); None diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs index 69c05a76d..e47054756 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs @@ -85,7 +85,7 @@ pub(crate) fn complete_type_path( let module_scope = module.scope(ctx.db, Some(ctx.module)); for (name, def) in module_scope { if scope_def_applicable(def) { - acc.add_path_resolution(ctx, path_ctx, name, def); + acc.add_path_resolution(ctx, path_ctx, name, def, vec![]); } } } @@ -141,7 +141,7 @@ pub(crate) fn complete_type_path( match location { TypeLocation::TypeBound => { acc.add_nameref_keywords_with_colon(ctx); - ctx.process_all_names(&mut |name, res| { + ctx.process_all_names(&mut |name, res, doc_aliases| { let add_resolution = match res { ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => { mac.is_fn_like(ctx.db) @@ -152,7 +152,7 @@ pub(crate) fn complete_type_path( _ => false, }; if add_resolution { - acc.add_path_resolution(ctx, path_ctx, name, res); + acc.add_path_resolution(ctx, path_ctx, name, res, doc_aliases); } }); return; @@ -215,9 +215,9 @@ pub(crate) fn complete_type_path( }; acc.add_nameref_keywords_with_colon(ctx); - ctx.process_all_names(&mut |name, def| { + ctx.process_all_names(&mut |name, def, doc_aliases| { if scope_def_applicable(def) { - acc.add_path_resolution(ctx, path_ctx, name, def); + acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases); } }); } @@ -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/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs index 2555c34aa..7a60030e9 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs @@ -52,6 +52,9 @@ pub(crate) fn complete_use_path( ) }; for (name, def) in module_scope { + if !ctx.check_stability(def.attrs(ctx.db).as_deref()) { + continue; + } let is_name_already_imported = name .as_text() .map_or(false, |text| already_imported_names.contains(text.as_str())); @@ -72,7 +75,7 @@ pub(crate) fn complete_use_path( is_name_already_imported, ..Default::default() }); - acc.add(builder.build()); + acc.add(builder.build(ctx.db)); } } } @@ -91,10 +94,10 @@ pub(crate) fn complete_use_path( // only show modules and non-std enum in a fresh UseTree Qualified::No => { cov_mark::hit!(unqualified_path_selected_only); - ctx.process_all_names(&mut |name, res| { + ctx.process_all_names(&mut |name, res, doc_aliases| { match res { ScopeDef::ModuleDef(hir::ModuleDef::Module(module)) => { - acc.add_module(ctx, path_ctx, module, name); + acc.add_module(ctx, path_ctx, module, name, doc_aliases); } ScopeDef::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(e))) => { // exclude prelude enum @@ -105,9 +108,9 @@ pub(crate) fn complete_use_path( let item = CompletionItem::new( CompletionItemKind::SymbolKind(SymbolKind::Enum), ctx.source_range(), - format!("{}::", e.name(ctx.db)), + format!("{}::", e.name(ctx.db).display(ctx.db)), ); - acc.add(item.build()); + acc.add(item.build(ctx.db)); } } _ => {} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs index 5e6cf4bf9..e0a959ad0 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs @@ -23,7 +23,7 @@ pub(crate) fn complete_vis_path( if let Some(next) = next_towards_current { if let Some(name) = next.name(ctx.db) { cov_mark::hit!(visibility_qualified); - acc.add_module(ctx, path_ctx, next, name); + acc.add_module(ctx, path_ctx, next, name, vec![]); } } 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 8cbf89e9c..7b145f3c1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -17,7 +17,7 @@ use ide_db::{ }; use syntax::{ ast::{self, AttrKind, NameOrNameRef}, - AstNode, + AstNode, SmolStr, SyntaxKind::{self, *}, SyntaxToken, TextRange, TextSize, T, }; @@ -367,6 +367,8 @@ pub(crate) struct CompletionContext<'a> { pub(super) krate: hir::Crate, /// The module of the `scope`. pub(super) module: hir::Module, + /// Whether nightly toolchain is used. Cached since this is looked up a lot. + is_nightly: bool, /// The expected name of what we are completing. /// This is usually the parameter name of the function argument we are completing. @@ -386,7 +388,7 @@ pub(crate) struct CompletionContext<'a> { pub(super) depth_from_crate_root: usize, } -impl<'a> CompletionContext<'a> { +impl CompletionContext<'_> { /// The range of the identifier that is being completed. pub(crate) fn source_range(&self) -> TextRange { let kind = self.original_token.kind(); @@ -441,6 +443,14 @@ impl<'a> CompletionContext<'a> { self.is_visible_impl(&vis, &attrs, item.krate(self.db)) } + pub(crate) fn doc_aliases<I>(&self, item: &I) -> Vec<SmolStr> + where + I: hir::HasAttrs + Copy, + { + let attrs = item.attrs(self.db); + attrs.doc_aliases().collect() + } + /// Check if an item is `#[doc(hidden)]`. pub(crate) fn is_item_hidden(&self, item: &hir::ItemInNs) -> bool { let attrs = item.attrs(self.db); @@ -451,6 +461,12 @@ impl<'a> CompletionContext<'a> { } } + /// Checks whether this item should be listed in regards to stability. Returns `true` if we should. + pub(crate) fn check_stability(&self, attrs: Option<&hir::Attrs>) -> bool { + let Some(attrs) = attrs else { return true; }; + !attrs.is_unstable() || self.is_nightly + } + /// Whether the given trait is an operator trait or not. pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool { match trait_.attrs(self.db).lang() { @@ -491,21 +507,22 @@ impl<'a> CompletionContext<'a> { ); } - /// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items. - pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) { + /// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items and + /// passes all doc-aliases along, to funnel it into [`Completions::add_path_resolution`]. + pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef, Vec<SmolStr>)) { let _p = profile::span("CompletionContext::process_all_names"); self.scope.process_all_names(&mut |name, def| { if self.is_scope_def_hidden(def) { return; } - - f(name, def); + let doc_aliases = self.doc_aliases_in_scope(def); + f(name, def, doc_aliases); }); } pub(crate) fn process_all_names_raw(&self, f: &mut dyn FnMut(Name, ScopeDef)) { let _p = profile::span("CompletionContext::process_all_names_raw"); - self.scope.process_all_names(&mut |name, def| f(name, def)); + self.scope.process_all_names(f); } fn is_scope_def_hidden(&self, scope_def: ScopeDef) -> bool { @@ -545,6 +562,14 @@ impl<'a> CompletionContext<'a> { // `doc(hidden)` items are only completed within the defining crate. self.krate != defining_crate && attrs.has_doc_hidden() } + + pub(crate) fn doc_aliases_in_scope(&self, scope_def: ScopeDef) -> Vec<SmolStr> { + if let Some(attrs) = scope_def.attrs(self.db) { + attrs.doc_aliases().collect() + } else { + vec![] + } + } } // CompletionContext construction @@ -615,6 +640,11 @@ impl<'a> CompletionContext<'a> { let krate = scope.krate(); let module = scope.module(); + let toolchain = db.crate_graph()[krate.into()].channel; + // `toolchain == None` means we're in some detached files. Since we have no information on + // the toolchain being used, let's just allow unstable items to be listed. + let is_nightly = matches!(toolchain, Some(base_db::ReleaseChannel::Nightly) | None); + let mut locals = FxHashMap::default(); scope.process_all_names(&mut |name, scope| { if let ScopeDef::Local(local) = scope { @@ -634,6 +664,7 @@ impl<'a> CompletionContext<'a> { token, krate, module, + is_nightly, expected_name, expected_type, qualifier_ctx, 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 a94c40458..cc5221cfc 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 @@ -609,14 +609,14 @@ fn classify_name_ref( _ => false, }; - let reciever_is_part_of_indivisible_expression = match &receiver { + let receiver_is_part_of_indivisible_expression = match &receiver { Some(ast::Expr::IfExpr(_)) => { let next_token_kind = next_non_trivia_token(name_ref.syntax().clone()).map(|t| t.kind()); next_token_kind == Some(SyntaxKind::ELSE_KW) }, _ => false }; - if reciever_is_part_of_indivisible_expression { + if receiver_is_part_of_indivisible_expression { return None; } @@ -1190,7 +1190,7 @@ fn pattern_context_for( }) }).and_then(|variants| { Some(variants.iter().filter_map(|variant| { - let variant_name = variant.name(sema.db).to_string(); + let variant_name = variant.name(sema.db).display(sema.db).to_string(); let variant_already_present = match_arm_list.arms().any(|arm| { arm.pat().and_then(|pat| { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index bb9fa7cca..e850f7bfd 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -3,7 +3,8 @@ use std::fmt; use hir::{Documentation, Mutability}; -use ide_db::{imports::import_assets::LocatedImport, SnippetCap, SymbolKind}; +use ide_db::{imports::import_assets::LocatedImport, RootDatabase, SnippetCap, SymbolKind}; +use itertools::Itertools; use smallvec::SmallVec; use stdx::{impl_from, never}; use syntax::{SmolStr, TextRange, TextSize}; @@ -45,7 +46,7 @@ pub struct CompletionItem { /// /// That is, in `foo.bar$0` lookup of `abracadabra` will be accepted (it /// contains `bar` sub sequence), and `quux` will rejected. - pub lookup: Option<SmolStr>, + pub lookup: SmolStr, /// Additional info to show in the UI pop up. pub detail: Option<String>, @@ -75,7 +76,8 @@ pub struct CompletionItem { pub ref_match: Option<(Mutability, TextSize)>, /// The import data to add to completion's edits. - pub import_to_add: SmallVec<[LocatedImport; 1]>, + /// (ImportPath, LastSegment) + pub import_to_add: SmallVec<[(String, String); 1]>, } // We use custom debug for CompletionItem to make snapshot tests more readable. @@ -353,12 +355,13 @@ impl CompletionItem { relevance: CompletionRelevance::default(), ref_match: None, imports_to_add: Default::default(), + doc_aliases: vec![], } } /// What string is used for filtering. pub fn lookup(&self) -> &str { - self.lookup.as_deref().unwrap_or(&self.label) + self.lookup.as_str() } pub fn ref_match(&self) -> Option<(String, text_edit::Indel, CompletionRelevance)> { @@ -385,6 +388,7 @@ pub(crate) struct Builder { source_range: TextRange, imports_to_add: SmallVec<[LocatedImport; 1]>, trait_name: Option<SmolStr>, + doc_aliases: Vec<SmolStr>, label: SmolStr, insert_text: Option<String>, is_snippet: bool, @@ -406,21 +410,31 @@ impl Builder { local_name: hir::Name, resolution: hir::ScopeDef, ) -> Self { - render_path_resolution(RenderContext::new(ctx), path_ctx, local_name, resolution) + let doc_aliases = ctx.doc_aliases_in_scope(resolution); + render_path_resolution( + RenderContext::new(ctx).doc_aliases(doc_aliases), + path_ctx, + local_name, + resolution, + ) } - pub(crate) fn build(self) -> CompletionItem { + pub(crate) fn build(self, db: &RootDatabase) -> CompletionItem { let _p = profile::span("item::Builder::build"); let mut label = self.label; - let mut lookup = self.lookup; + let mut lookup = self.lookup.unwrap_or_else(|| label.clone()); let insert_text = self.insert_text.unwrap_or_else(|| label.to_string()); + if !self.doc_aliases.is_empty() { + let doc_aliases = self.doc_aliases.into_iter().join(", "); + label = SmolStr::from(format!("{label} (alias {doc_aliases})")); + lookup = SmolStr::from(format!("{lookup} {doc_aliases}")); + } if let [import_edit] = &*self.imports_to_add { // snippets can have multiple imports, but normal completions only have up to one if let Some(original_path) = import_edit.original_path.as_ref() { - lookup = lookup.or_else(|| Some(label.clone())); - label = SmolStr::from(format!("{label} (use {original_path})")); + label = SmolStr::from(format!("{label} (use {})", original_path.display(db))); } } else if let Some(trait_name) = self.trait_name { label = SmolStr::from(format!("{label} (as {trait_name})")); @@ -431,6 +445,17 @@ impl Builder { None => TextEdit::replace(self.source_range, insert_text), }; + let import_to_add = self + .imports_to_add + .into_iter() + .filter_map(|import| { + Some(( + import.import_path.display(db).to_string(), + import.import_path.segments().last()?.display(db).to_string(), + )) + }) + .collect(); + CompletionItem { source_range: self.source_range, label, @@ -444,7 +469,7 @@ impl Builder { trigger_call_info: self.trigger_call_info, relevance: self.relevance, ref_match: self.ref_match, - import_to_add: self.imports_to_add, + import_to_add, } } pub(crate) fn lookup_by(&mut self, lookup: impl Into<SmolStr>) -> &mut Builder { @@ -459,6 +484,10 @@ impl Builder { self.trait_name = Some(trait_name); self } + pub(crate) fn doc_aliases(&mut self, doc_aliases: Vec<SmolStr>) -> &mut Builder { + self.doc_aliases = doc_aliases; + self + } pub(crate) fn insert_text(&mut self, insert_text: impl Into<String>) -> &mut Builder { self.insert_text = Some(insert_text.into()); self diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs index 6fe781114..106d4e1e5 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs @@ -97,7 +97,7 @@ pub use crate::{ /// Main entry point for completion. We run completion as a two-phase process. /// -/// First, we look at the position and collect a so-called `CompletionContext. +/// First, we look at the position and collect a so-called `CompletionContext`. /// This is a somewhat messy process, because, during completion, syntax tree is /// incomplete and can look really weird. /// @@ -133,7 +133,7 @@ pub use crate::{ /// /// Another case where this would be instrumental is macro expansion. We want to /// insert a fake ident and re-expand code. There's `expand_speculative` as a -/// work-around for this. +/// workaround for this. /// /// A different use-case is completion of injection (examples and links in doc /// comments). When computing completion for a path in a doc-comment, you want @@ -243,7 +243,7 @@ pub fn resolve_completion_edits( config.prefer_no_std, ) }) - .find(|mod_path| mod_path.to_string() == full_import_path); + .find(|mod_path| mod_path.display(db).to_string() == full_import_path); if let Some(import_path) = import { insert_use::insert_use(&new_ast, mod_path_to_ast(&import_path), &config.insert_use); } 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 c1f51aabb..1953eb479 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -32,11 +32,17 @@ pub(crate) struct RenderContext<'a> { completion: &'a CompletionContext<'a>, is_private_editable: bool, import_to_add: Option<LocatedImport>, + doc_aliases: Vec<SmolStr>, } impl<'a> RenderContext<'a> { pub(crate) fn new(completion: &'a CompletionContext<'a>) -> RenderContext<'a> { - RenderContext { completion, is_private_editable: false, import_to_add: None } + RenderContext { + completion, + is_private_editable: false, + import_to_add: None, + doc_aliases: vec![], + } } pub(crate) fn private_editable(mut self, private_editable: bool) -> Self { @@ -49,6 +55,11 @@ impl<'a> RenderContext<'a> { self } + pub(crate) fn doc_aliases(mut self, doc_aliases: Vec<SmolStr>) -> Self { + self.doc_aliases = doc_aliases; + self + } + fn snippet_cap(&self) -> Option<SnippetCap> { self.completion.config.snippet_cap } @@ -115,24 +126,25 @@ pub(crate) fn render_field( field: hir::Field, ty: &hir::Type, ) -> CompletionItem { + let db = ctx.db(); let is_deprecated = ctx.is_deprecated(field); - let name = field.name(ctx.db()); + let name = field.name(db); let (name, escaped_name) = (name.unescaped().to_smol_str(), name.to_smol_str()); let mut item = CompletionItem::new( SymbolKind::Field, ctx.source_range(), - field_with_receiver(receiver.as_ref(), &name), + field_with_receiver(db, receiver.as_ref(), &name), ); item.set_relevance(CompletionRelevance { type_match: compute_type_match(ctx.completion, ty), exact_name_match: compute_exact_name_match(ctx.completion, name.as_str()), ..CompletionRelevance::default() }); - item.detail(ty.display(ctx.db()).to_string()) - .set_documentation(field.docs(ctx.db())) + item.detail(ty.display(db).to_string()) + .set_documentation(field.docs(db)) .set_deprecated(is_deprecated) .lookup_by(name); - item.insert_text(field_with_receiver(receiver.as_ref(), &escaped_name)); + item.insert_text(field_with_receiver(db, receiver.as_ref(), &escaped_name)); if let Some(receiver) = &dot_access.receiver { if let Some(original) = ctx.completion.sema.original_ast_node(receiver.clone()) { if let Some(ref_match) = compute_ref_match(ctx.completion, ty) { @@ -140,11 +152,19 @@ pub(crate) fn render_field( } } } - item.build() + item.doc_aliases(ctx.doc_aliases); + item.build(db) } -fn field_with_receiver(receiver: Option<&hir::Name>, field_name: &str) -> SmolStr { - receiver.map_or_else(|| field_name.into(), |receiver| format!("{receiver}.{field_name}").into()) +fn field_with_receiver( + db: &RootDatabase, + receiver: Option<&hir::Name>, + field_name: &str, +) -> SmolStr { + receiver.map_or_else( + || field_name.into(), + |receiver| format!("{}.{field_name}", receiver.display(db)).into(), + ) } pub(crate) fn render_tuple_field( @@ -156,10 +176,10 @@ pub(crate) fn render_tuple_field( let mut item = CompletionItem::new( SymbolKind::Field, ctx.source_range(), - field_with_receiver(receiver.as_ref(), &field.to_string()), + field_with_receiver(ctx.db(), receiver.as_ref(), &field.to_string()), ); item.detail(ty.display(ctx.db()).to_string()).lookup_by(field.to_string()); - item.build() + item.build(ctx.db()) } pub(crate) fn render_type_inference( @@ -169,7 +189,7 @@ pub(crate) fn render_type_inference( let mut builder = CompletionItem::new(CompletionItemKind::InferredType, ctx.source_range(), ty_string); builder.set_relevance(CompletionRelevance { is_definite: true, ..Default::default() }); - builder.build() + builder.build(ctx.db) } pub(crate) fn render_path_resolution( @@ -197,7 +217,9 @@ pub(crate) fn render_resolution_with_import( ) -> Option<Builder> { let resolution = ScopeDef::from(import_edit.original_item); let local_name = scope_def_to_name(resolution, &ctx, &import_edit)?; - + //this now just renders the alias text, but we need to find the aliases earlier and call this with the alias instead + let doc_aliases = ctx.completion.doc_aliases_in_scope(resolution); + let ctx = ctx.doc_aliases(doc_aliases); Some(render_resolution_path(ctx, path_ctx, local_name, Some(import_edit), resolution)) } @@ -305,7 +327,7 @@ fn render_resolution_path( item.lookup_by(name.clone()) .label(SmolStr::from_iter([&name, "<…>"])) .trigger_call_info() - .insert_snippet(cap, format!("{local_name}<$0>")); + .insert_snippet(cap, format!("{}<$0>", local_name.display(db))); } } } @@ -348,6 +370,8 @@ fn render_resolution_simple_( if let Some(import_to_add) = ctx.import_to_add { item.add_import(import_to_add); } + + item.doc_aliases(ctx.doc_aliases); item } 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 70b19988c..3c73983c3 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 @@ -29,5 +29,5 @@ fn render(ctx: RenderContext<'_>, const_: hir::Const) -> Option<CompletionItem> } item.insert_text(escaped_name); - Some(item.build()) + Some(item.build(ctx.db())) } 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 197592e78..8afce8db5 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,8 +52,13 @@ fn render( let (call, escaped_call) = match &func_kind { FuncKind::Method(_, Some(receiver)) => ( - format!("{}.{}", receiver.unescaped(), name.unescaped()).into(), - format!("{receiver}.{name}").into(), + format!( + "{}.{}", + receiver.unescaped().display(ctx.db()), + name.unescaped().display(ctx.db()) + ) + .into(), + format!("{}.{}", receiver.display(ctx.db()), name.display(ctx.db())).into(), ), _ => (name.unescaped().to_smol_str(), name.to_smol_str()), }; @@ -147,6 +152,8 @@ fn render( } } } + + item.doc_aliases(ctx.doc_aliases); item } 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 ed78fcd8e..728d236df 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 @@ -71,8 +71,10 @@ fn render( } None => (name.clone().into(), name.into(), false), }; - let (qualified_name, escaped_qualified_name) = - (qualified_name.unescaped().to_string(), qualified_name.to_string()); + let (qualified_name, escaped_qualified_name) = ( + qualified_name.unescaped().display(ctx.db()).to_string(), + qualified_name.display(ctx.db()).to_string(), + ); let snippet_cap = ctx.snippet_cap(); let mut rendered = match kind { @@ -98,7 +100,7 @@ fn render( } let label = format_literal_label(&qualified_name, kind, snippet_cap); let lookup = if qualified { - format_literal_lookup(&short_qualified_name.to_string(), kind) + format_literal_lookup(&short_qualified_name.display(ctx.db()).to_string(), kind) } else { format_literal_lookup(&qualified_name, kind) }; 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 44e886076..ce7af1d34 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 @@ -74,7 +74,7 @@ fn render( item.insert_text(banged_name(&escaped_name)).lookup_by(banged_name(&name)); } _ => { - cov_mark::hit!(dont_insert_macro_call_parens_unncessary); + cov_mark::hit!(dont_insert_macro_call_parens_unnecessary); item.insert_text(escaped_name); } }; @@ -140,8 +140,8 @@ mod tests { use crate::tests::check_edit; #[test] - fn dont_insert_macro_call_parens_unncessary() { - cov_mark::check!(dont_insert_macro_call_parens_unncessary); + fn dont_insert_macro_call_parens_unnecessary() { + cov_mark::check!(dont_insert_macro_call_parens_unnecessary); check_edit( "frobnicate", r#" 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 9225c91be..d06abc5e9 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 @@ -57,7 +57,10 @@ pub(crate) fn render_variant_pat( let enum_ty = variant.parent_enum(ctx.db()).ty(ctx.db()); let (name, escaped_name) = match path { - Some(path) => (path.unescaped().to_string().into(), path.to_string().into()), + Some(path) => ( + path.unescaped().display(ctx.db()).to_string().into(), + path.display(ctx.db()).to_string().into(), + ), None => { let name = local_name.unwrap_or_else(|| variant.name(ctx.db())); (name.unescaped().to_smol_str(), name.to_smol_str()) @@ -121,7 +124,7 @@ fn build_completion( Some(snippet_cap) => item.insert_snippet(snippet_cap, pat), None => item.insert_text(pat), }; - item.build() + item.build(ctx.db()) } fn render_pat( @@ -172,7 +175,7 @@ fn render_record_as_pat( format!( "{name} {{ {}{} }}", fields.enumerate().format_with(", ", |(idx, field), f| { - f(&format_args!("{}${}", field.name(db), idx + 1)) + f(&format_args!("{}${}", field.name(db).display(db.upcast()), idx + 1)) }), 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 fbe120d2a..343ba7e28 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 @@ -53,5 +53,5 @@ fn render( } item.insert_text(escaped_name); - Some(item.build()) + Some(item.build(ctx.db())) } 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 6e0c53ec9..93e943dbe 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 @@ -21,8 +21,10 @@ 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.unescaped().to_string(), p.to_string()), - None => (name.unescaped().to_string(), name.to_string()), + Some(p) => (p.unescaped().display(ctx.db()).to_string(), p.display(ctx.db()).to_string()), + None => { + (name.unescaped().display(ctx.db()).to_string(), name.display(ctx.db()).to_string()) + } }; let label = format_literal_label(&name.to_smol_str(), StructKind::Record, ctx.snippet_cap()); let lookup = format_literal_lookup(&name.to_smol_str(), StructKind::Record); @@ -51,9 +53,9 @@ pub(crate) fn render_union_literal( format!( "{} {{ {} }}", escaped_qualified_name, - fields - .iter() - .format_with(", ", |field, f| { f(&format_args!("{}: ()", field.name(ctx.db()))) }) + fields.iter().format_with(", ", |field, f| { + f(&format_args!("{}: ()", field.name(ctx.db()).display(ctx.db()))) + }) ) }; @@ -61,7 +63,11 @@ pub(crate) fn render_union_literal( "{} {{ {}{} }}", qualified_name, fields.iter().format_with(", ", |field, f| { - f(&format_args!("{}: {}", field.name(ctx.db()), field.ty(ctx.db()).display(ctx.db()))) + f(&format_args!( + "{}: {}", + field.name(ctx.db()).display(ctx.db()), + field.ty(ctx.db()).display(ctx.db()) + )) }), if fields_omitted { ", .." } else { "" } ); @@ -76,5 +82,5 @@ pub(crate) fn render_union_literal( None => item.insert_text(literal), }; - Some(item.build()) + Some(item.build(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 55c55725b..a9a01a3a3 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 @@ -27,14 +27,14 @@ pub(crate) fn render_record_lit( } let completions = fields.iter().enumerate().format_with(", ", |(idx, field), f| { if snippet_cap.is_some() { - f(&format_args!("{}: ${{{}:()}}", field.name(db), idx + 1)) + f(&format_args!("{}: ${{{}:()}}", field.name(db).display(db.upcast()), idx + 1)) } else { - f(&format_args!("{}: ()", field.name(db))) + f(&format_args!("{}: ()", field.name(db).display(db.upcast()))) } }); let types = fields.iter().format_with(", ", |field, f| { - f(&format_args!("{}: {}", field.name(db), field.ty(db).display(db))) + f(&format_args!("{}: {}", field.name(db).display(db.upcast()), field.ty(db).display(db))) }); RenderedLiteral { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs index 1fe48b9e9..2464e8d5f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs @@ -23,7 +23,8 @@ mod type_pos; mod use_tree; mod visibility; -use hir::{db::DefDatabase, PrefixKind}; +use expect_test::Expect; +use hir::PrefixKind; use ide_db::{ base_db::{fixture::ChangeFixture, FileLoader, FilePosition}, imports::insert_use::{ImportGranularity, InsertUseConfig}, @@ -104,7 +105,7 @@ fn completion_list_with_config( include_keywords: bool, trigger_character: Option<char>, ) -> String { - // filter out all but one builtintype completion for smaller test outputs + // filter out all but one built-in type completion for smaller test outputs let items = get_all_items(config, ra_fixture, trigger_character); let items = items .into_iter() @@ -120,7 +121,7 @@ fn completion_list_with_config( pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) { let change_fixture = ChangeFixture::parse(ra_fixture); let mut database = RootDatabase::default(); - database.set_enable_proc_attr_macros(true); + database.enable_proc_attr_macros(); database.apply_change(change_fixture.change); let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); let offset = range_or_offset.expect_offset(); @@ -197,11 +198,11 @@ pub(crate) fn check_edit_with_config( &db, &config, position, - completion.import_to_add.iter().filter_map(|import_edit| { - let import_path = &import_edit.import_path; - let import_name = import_path.segments().last()?; - Some((import_path.to_string(), import_name.to_string())) - }), + completion + .import_to_add + .iter() + .cloned() + .filter_map(|(import_path, import_name)| Some((import_path, import_name))), ) .into_iter() .flatten() @@ -215,6 +216,11 @@ pub(crate) fn check_edit_with_config( assert_eq_text!(&ra_fixture_after, &actual) } +fn check_empty(ra_fixture: &str, expect: Expect) { + let actual = completion_list(ra_fixture); + expect.assert_eq(&actual); +} + pub(crate) fn get_all_items( config: CompletionConfig, code: &str, 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 c1c6a689e..be5b7f8a3 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,18 +1,13 @@ //! Completion tests for expressions. use expect_test::{expect, Expect}; -use crate::tests::{check_edit, completion_list, BASE_ITEMS_FIXTURE}; +use crate::tests::{check_edit, check_empty, completion_list, BASE_ITEMS_FIXTURE}; fn check(ra_fixture: &str, expect: Expect) { let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}")); expect.assert_eq(&actual) } -fn check_empty(ra_fixture: &str, expect: Expect) { - let actual = completion_list(ra_fixture); - expect.assert_eq(&actual); -} - #[test] fn complete_literal_struct_with_a_private_field() { // `FooDesc.bar` is private, the completion should not be triggered. @@ -672,7 +667,7 @@ fn main() { } #[test] -fn varaiant_with_struct() { +fn variant_with_struct() { check_empty( r#" pub struct YoloVariant { @@ -997,3 +992,105 @@ fn foo() { if foo {} el$0 { let x = 92; } } "#]], ); } + +#[test] +fn expr_no_unstable_item_on_stable() { + check_empty( + r#" +//- /main.rs crate:main deps:std +use std::*; +fn main() { + $0 +} +//- /std.rs crate:std +#[unstable] +pub struct UnstableThisShouldNotBeListed; +"#, + expect![[r#" + fn main() fn() + md std + bt u32 + kw const + kw crate:: + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw let + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); +} + +#[test] +fn expr_unstable_item_on_nightly() { + check_empty( + r#" +//- toolchain:nightly +//- /main.rs crate:main deps:std +use std::*; +fn main() { + $0 +} +//- /std.rs crate:std +#[unstable] +pub struct UnstableButWeAreOnNightlyAnyway; +"#, + expect![[r#" + fn main() fn() + md std + st UnstableButWeAreOnNightlyAnyway + bt u32 + kw const + kw crate:: + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw let + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); +} 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 0b485eb77..8c038c0fb 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 @@ -1108,6 +1108,41 @@ fn function() { } #[test] +fn flyimport_pattern_no_unstable_item_on_stable() { + check( + r#" +//- /main.rs crate:main deps:std +fn function() { + let foo$0 +} +//- /std.rs crate:std +#[unstable] +pub struct FooStruct {} +"#, + expect![""], + ); +} + +#[test] +fn flyimport_pattern_unstable_item_on_nightly() { + check( + r#" +//- toolchain:nightly +//- /main.rs crate:main deps:std +fn function() { + let foo$0 +} +//- /std.rs crate:std +#[unstable] +pub struct FooStruct {} +"#, + expect![[r#" + st FooStruct (use std::FooStruct) + "#]], + ); +} + +#[test] fn flyimport_item_name() { check( r#" @@ -1230,3 +1265,24 @@ macro_rules! define_struct { "#]], ); } + +#[test] +fn macro_use_prelude_is_in_scope() { + check( + r#" +//- /main.rs crate:main deps:dep +#[macro_use] +extern crate dep; + +fn main() { + print$0 +} +//- /lib.rs crate:dep +#[macro_export] +macro_rules! println { + () => {} +} +"#, + expect![""], + ) +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs index 9fc731bb1..2b5b4dd77 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs @@ -1,7 +1,7 @@ //! Completion tests for item list position. use expect_test::{expect, Expect}; -use crate::tests::{check_edit, completion_list, BASE_ITEMS_FIXTURE}; +use crate::tests::{check_edit, check_empty, completion_list, BASE_ITEMS_FIXTURE}; fn check(ra_fixture: &str, expect: Expect) { let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}")); @@ -298,6 +298,58 @@ impl Test for () { } #[test] +fn in_trait_impl_no_unstable_item_on_stable() { + check_empty( + r#" +trait Test { + #[unstable] + type Type; + #[unstable] + const CONST: (); + #[unstable] + fn function(); +} + +impl Test for () { + $0 +} +"#, + expect![[r#" + kw crate:: + kw self:: + "#]], + ); +} + +#[test] +fn in_trait_impl_unstable_item_on_nightly() { + check_empty( + r#" +//- toolchain:nightly +trait Test { + #[unstable] + type Type; + #[unstable] + const CONST: (); + #[unstable] + fn function(); +} + +impl Test for () { + $0 +} +"#, + expect![[r#" + ct const CONST: () = + fn fn function() + ta type Type = + kw crate:: + kw self:: + "#]], + ); +} + +#[test] fn after_unit_struct() { check( r#"struct S; f$0"#, 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 c0e485c36..8af6cce98 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 @@ -1,12 +1,7 @@ //! Completion tests for pattern position. use expect_test::{expect, Expect}; -use crate::tests::{check_edit, completion_list, BASE_ITEMS_FIXTURE}; - -fn check_empty(ra_fixture: &str, expect: Expect) { - let actual = completion_list(ra_fixture); - expect.assert_eq(&actual) -} +use crate::tests::{check_edit, check_empty, completion_list, BASE_ITEMS_FIXTURE}; fn check(ra_fixture: &str, expect: Expect) { let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}\n{ra_fixture}")); @@ -742,3 +737,56 @@ fn f(x: EnumAlias<u8>) { "#]], ); } + +#[test] +fn pat_no_unstable_item_on_stable() { + check_empty( + r#" +//- /main.rs crate:main deps:std +use std::*; +fn foo() { + let a$0 +} +//- /std.rs crate:std +#[unstable] +pub struct S; +#[unstable] +pub enum Enum { + Variant +} +"#, + expect![[r#" + md std + kw mut + kw ref + "#]], + ); +} + +#[test] +fn pat_unstable_item_on_nightly() { + check_empty( + r#" +//- toolchain:nightly +//- /main.rs crate:main deps:std +use std::*; +fn foo() { + let a$0 +} +//- /std.rs crate:std +#[unstable] +pub struct S; +#[unstable] +pub enum Enum { + Variant +} +"#, + expect![[r#" + en Enum + md std + st S + kw mut + kw ref + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs index 2656a4d54..789ad6634 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs @@ -1,7 +1,7 @@ //! Completion tests for predicates and bounds. use expect_test::{expect, Expect}; -use crate::tests::{completion_list, BASE_ITEMS_FIXTURE}; +use crate::tests::{check_empty, completion_list, BASE_ITEMS_FIXTURE}; fn check(ra_fixture: &str, expect: Expect) { let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}\n{ra_fixture}")); @@ -129,3 +129,43 @@ impl Record { "#]], ); } + +#[test] +fn pred_no_unstable_item_on_stable() { + check_empty( + r#" +//- /main.rs crate:main deps:std +use std::*; +struct Foo<T> where T: $0 {} +//- /std.rs crate:std +#[unstable] +pub trait Trait {} +"#, + expect![[r#" + md std + kw crate:: + kw self:: + "#]], + ); +} + +#[test] +fn pred_unstable_item_on_nightly() { + check_empty( + r#" +//- toolchain:nightly +//- /main.rs crate:main deps:std +use std::*; +struct Foo<T> where T: $0 {} +//- /std.rs crate:std +#[unstable] +pub trait Trait {} +"#, + expect![[r#" + md std + tt Trait + kw crate:: + kw self:: + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/proc_macros.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/proc_macros.rs index 92ea4d15b..2d6234e31 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/proc_macros.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/proc_macros.rs @@ -81,7 +81,7 @@ impl Foo { } #[proc_macros::input_replace( - fn suprise() { + fn surprise() { Foo.$0 } )] @@ -114,7 +114,7 @@ impl Foo { } #[proc_macros::input_replace( - fn suprise() { + fn surprise() { Foo.f$0 } )] diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs index f8a6f6cd3..382472083 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs @@ -989,3 +989,294 @@ fn foo { crate::::$0 } expect![""], ) } + +#[test] +fn completes_struct_via_doc_alias_in_fn_body() { + check( + r#" +#[doc(alias = "Bar")] +struct Foo; + +fn here_we_go() { + $0 +} +"#, + expect![[r#" + fn here_we_go() fn() + st Foo (alias Bar) + bt u32 + kw const + kw crate:: + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw let + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); +} + +#[test] +fn completes_struct_via_multiple_doc_aliases_in_fn_body() { + check( + r#" +#[doc(alias("Bar", "Qux"))] +#[doc(alias = "Baz")] +struct Foo; + +fn here_we_go() { + B$0 +} +"#, + expect![[r#" + fn here_we_go() fn() + st Foo (alias Bar, Qux, Baz) + bt u32 + kw const + kw crate:: + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw let + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); +} + +#[test] +fn completes_field_name_via_doc_alias_in_fn_body() { + check( + r#" +struct Foo { + #[doc(alias = "qux")] + bar: u8 +}; + +fn here_we_go() { + let foo = Foo { q$0 } +} +"#, + expect![[r#" + fd bar (alias qux) u8 + "#]], + ); +} + +#[test] +fn completes_struct_fn_name_via_doc_alias_in_fn_body() { + check( + r#" +struct Foo; +impl Foo { + #[doc(alias = "qux")] + fn bar() -> u8 { 1 } +} + +fn here_we_go() { + Foo::q$0 +} +"#, + expect![[r#" + fn bar() (alias qux) fn() -> u8 + "#]], + ); +} + +#[test] +fn completes_method_name_via_doc_alias_in_fn_body() { + check( + r#" +struct Foo { + bar: u8 +} +impl Foo { + #[doc(alias = "qux")] + fn baz(&self) -> u8 { + self.bar + } +} + +fn here_we_go() { + let foo = Foo { field: 42 }; + foo.q$0 +} +"#, + expect![[r#" + fd bar u8 + me baz() (alias qux) fn(&self) -> u8 + 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 + sn unsafe unsafe {} + "#]], + ); +} + +#[test] +fn completes_fn_name_via_doc_alias_in_fn_body() { + check( + r#" +#[doc(alias = "qux")] +fn foo() {} +fn bar() { qu$0 } +"#, + expect![[r#" + fn bar() fn() + fn foo() (alias qux) fn() + bt u32 + kw const + kw crate:: + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw let + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); +} + +#[test] +fn completes_struct_name_via_doc_alias_in_another_mod() { + check( + r#" +mod foo { + #[doc(alias = "Qux")] + pub struct Bar(u8); +} + +fn here_we_go() { + use foo; + let foo = foo::Q$0 +} +"#, + expect![[r#" + st Bar (alias Qux) + "#]], + ); +} + +#[test] +fn completes_use_via_doc_alias_in_another_mod() { + check( + r#" +mod foo { + #[doc(alias = "Qux")] + pub struct Bar(u8); +} + +fn here_we_go() { + use foo::Q$0; +} +"#, + expect![[r#" + st Bar (alias Qux) + "#]], + ); +} + +#[test] +fn completes_flyimport_with_doc_alias_in_another_mod() { + check( + r#" +mod foo { + #[doc(alias = "Qux")] + pub struct Bar(); +} + +fn here_we_go() { + let foo = Bar$0 +} +"#, + expect![[r#" + fn here_we_go() fn() + md foo + st Bar (alias Qux) (use foo::Bar) + 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 + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs index c3f4fb4d1..8cb1ff4a1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs @@ -1,7 +1,7 @@ //! Completion tests for type position. use expect_test::{expect, Expect}; -use crate::tests::{completion_list, BASE_ITEMS_FIXTURE}; +use crate::tests::{check_empty, completion_list, BASE_ITEMS_FIXTURE}; fn check(ra_fixture: &str, expect: Expect) { let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}\n{ra_fixture}")); @@ -669,3 +669,53 @@ fn f(t: impl MyTrait<Item1 = u8, Item2 = $0 "#]], ); } + +#[test] +fn type_pos_no_unstable_type_on_stable() { + check_empty( + r#" +//- /main.rs crate:main deps:std +use std::*; +struct Foo { + f: $0 +} +//- /std.rs crate:std +#[unstable] +pub struct S; +"#, + expect![[r#" + md std + sp Self + st Foo + bt u32 + kw crate:: + kw self:: + "#]], + ) +} + +#[test] +fn type_pos_unstable_type_on_nightly() { + check_empty( + r#" +//- toolchain:nightly +//- /main.rs crate:main deps:std +use std::*; +struct Foo { + f: $0 +} +//- /std.rs crate:std +#[unstable] +pub struct S; +"#, + expect![[r#" + md std + sp Self + st Foo + st S + bt u32 + kw crate:: + kw self:: + "#]], + ) +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs index 037d7dce5..4c74dba52 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs @@ -382,3 +382,50 @@ use self::foo::impl$0 "#]], ); } + +#[test] +fn use_tree_no_unstable_items_on_stable() { + check( + r#" +//- /lib.rs crate:main deps:std +use std::$0 +//- /std.rs crate:std +#[unstable] +pub mod simd {} +#[unstable] +pub struct S; +#[unstable] +pub fn foo() {} +#[unstable] +#[macro_export] +marco_rules! m { () => {} } +"#, + expect![""], + ); +} + +#[test] +fn use_tree_unstable_items_on_nightly() { + check( + r#" +//- toolchain:nightly +//- /lib.rs crate:main deps:std +use std::$0 +//- /std.rs crate:std +#[unstable] +pub mod simd {} +#[unstable] +pub struct S; +#[unstable] +pub fn foo() {} +#[unstable] +#[macro_export] +marco_rules! m { () => {} } +"#, + expect![[r#" + fn foo fn() + md simd + st S + "#]], + ); +} |