summaryrefslogtreecommitdiffstats
path: root/src/tools/rust-analyzer/crates/ide-completion/src
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/rust-analyzer/crates/ide-completion/src')
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions.rs132
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs161
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs18
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs84
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs149
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs47
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs14
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs12
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs13
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context.rs45
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/item.rs49
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/lib.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render.rs52
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs11
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs20
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests.rs22
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs111
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs56
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs54
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs60
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs42
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/proc_macros.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs291
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs52
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs47
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
+ "#]],
+ );
+}