diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 592e7d3b97..aa10b0f878 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -2,7 +2,7 @@ mod source_to_def; -use std::{cell::RefCell, fmt, iter}; +use std::{cell::RefCell, fmt, iter, ops}; use base_db::{FileId, FileRange}; use hir_def::{ @@ -1449,3 +1449,11 @@ impl<'a> SemanticsScope<'a> { } pub struct VisibleTraits(pub FxHashSet); + +impl ops::Deref for VisibleTraits { + type Target = FxHashSet; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index 147563ef10..81bb59681b 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -36,7 +36,7 @@ use crate::{ const_::render_const, function::{render_fn, render_method}, literal::{render_struct_literal, render_variant_lit}, - macro_::{render_macro, render_macro_pat}, + macro_::render_macro, pattern::{render_struct_pat, render_variant_pat}, render_field, render_path_resolution, render_pattern_resolution, render_tuple_field, type_alias::{render_type_alias, render_type_alias_with_eq}, @@ -101,15 +101,15 @@ impl Completions { pub(crate) fn add_keyword_snippet_expr( &mut self, ctx: &CompletionContext, + incomplete_let: bool, kw: &str, snippet: &str, - incomplete_let: bool, ) { let mut item = CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), kw); match ctx.config.snippet_cap { Some(cap) => { - if snippet.ends_with('}') && incomplete_let { + if incomplete_let && snippet.ends_with('}') { // complete block expression snippets with a trailing semicolon, if inside an incomplete let cov_mark::hit!(let_semi); item.insert_snippet(cap, format!("{};", snippet)); @@ -181,6 +181,17 @@ impl Completions { ); } + pub(crate) fn add_enum_variants( + &mut self, + ctx: &CompletionContext, + path_ctx: &PathCompletionCtx, + e: hir::Enum, + ) { + e.variants(ctx.db) + .into_iter() + .for_each(|variant| self.add_enum_variant(ctx, path_ctx, variant, None)); + } + pub(crate) fn add_module( &mut self, ctx: &CompletionContext, @@ -219,29 +230,6 @@ impl Completions { ); } - pub(crate) fn add_macro_pat( - &mut self, - ctx: &CompletionContext, - pattern_ctx: &PatternContext, - mac: hir::Macro, - local_name: hir::Name, - ) { - let is_private_editable = match ctx.is_visible(&mac) { - Visible::Yes => false, - Visible::Editable => true, - Visible::No => return, - }; - self.add( - render_macro_pat( - RenderContext::new(ctx).private_editable(is_private_editable), - pattern_ctx, - local_name, - mac, - ) - .build(), - ); - } - pub(crate) fn add_function( &mut self, ctx: &CompletionContext, diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs index bf0bce2198..911ef60930 100644 --- a/crates/ide-completion/src/completions/dot.rs +++ b/crates/ide-completion/src/completions/dot.rs @@ -121,7 +121,7 @@ fn complete_methods( receiver.iterate_method_candidates( ctx.db, &ctx.scope, - &ctx.traits_in_scope().0, + &ctx.traits_in_scope(), Some(ctx.module), None, |func| { diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs index 6b36801205..058d0ab7bb 100644 --- a/crates/ide-completion/src/completions/expr.rs +++ b/crates/ide-completion/src/completions/expr.rs @@ -1,7 +1,6 @@ //! Completion of names from the current scope in expression position. use hir::ScopeDef; -use ide_db::FxHashSet; use crate::{ context::{ExprCtx, PathCompletionCtx, Qualified}, @@ -33,24 +32,24 @@ pub(crate) fn complete_expr_path( let wants_mut_token = ref_expr_parent.as_ref().map(|it| it.mut_token().is_none()).unwrap_or(false); - let scope_def_applicable = |def| { - match def { - ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) | ScopeDef::Label(_) => { - false - } - // Don't suggest attribute macros and derives. - ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => mac.is_fn_like(ctx.db), - _ => true, - } + let scope_def_applicable = |def| match def { + ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) | ScopeDef::Label(_) => false, + ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => mac.is_fn_like(ctx.db), + _ => true, + }; + + let add_assoc_item = |acc: &mut Completions, item| match item { + hir::AssocItem::Function(func) => acc.add_function(ctx, path_ctx, func, None), + hir::AssocItem::Const(ct) => acc.add_const(ctx, ct), + hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), }; match qualified { Qualified::Infer => ctx .traits_in_scope() - .0 - .into_iter() - .flat_map(|it| hir::Trait::from(it).items(ctx.sema.db)) - .for_each(|item| add_assoc_item(acc, ctx, path_ctx, item)), + .iter() + .flat_map(|&it| hir::Trait::from(it).items(ctx.sema.db)) + .for_each(|item| add_assoc_item(acc, item)), Qualified::With { resolution: None, .. } => {} Qualified::With { resolution: Some(resolution), .. } => { // Add associated types on type parameters and `Self`. @@ -67,46 +66,32 @@ pub(crate) fn complete_expr_path( } } } - hir::PathResolution::Def( def @ (hir::ModuleDef::Adt(_) | hir::ModuleDef::TypeAlias(_) | hir::ModuleDef::BuiltinType(_)), ) => { - if let &hir::ModuleDef::Adt(hir::Adt::Enum(e)) = def { - add_enum_variants(acc, ctx, path_ctx, e); - } let ty = match def { hir::ModuleDef::Adt(adt) => adt.ty(ctx.db), - hir::ModuleDef::TypeAlias(a) => { - let ty = a.ty(ctx.db); - if let Some(hir::Adt::Enum(e)) = ty.as_adt() { - cov_mark::hit!(completes_variant_through_alias); - add_enum_variants(acc, ctx, path_ctx, e); - } - ty - } + hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db), hir::ModuleDef::BuiltinType(builtin) => { cov_mark::hit!(completes_primitive_assoc_const); builtin.ty(ctx.db) } - _ => unreachable!(), + _ => return, }; + if let Some(hir::Adt::Enum(e)) = ty.as_adt() { + cov_mark::hit!(completes_variant_through_alias); + acc.add_enum_variants(ctx, path_ctx, e); + } + // XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType. // (where AssocType is defined on a trait, not an inherent impl) - ty.iterate_path_candidates( - ctx.db, - &ctx.scope, - &ctx.traits_in_scope().0, - Some(ctx.module), - None, - |item| { - add_assoc_item(acc, ctx, path_ctx, item); - None::<()> - }, - ); + ctx.iterate_path_candidates(&ty, |item| { + add_assoc_item(acc, item); + }); // Iterate assoc types separately ty.iterate_assoc_items(ctx.db, ctx.krate, |item| { @@ -119,7 +104,7 @@ pub(crate) fn complete_expr_path( hir::PathResolution::Def(hir::ModuleDef::Trait(t)) => { // Handles `Trait::assoc` as well as `::assoc`. for item in t.items(ctx.db) { - add_assoc_item(acc, ctx, path_ctx, item); + add_assoc_item(acc, item); } } hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_) => { @@ -130,24 +115,12 @@ pub(crate) fn complete_expr_path( }; if let Some(hir::Adt::Enum(e)) = ty.as_adt() { - add_enum_variants(acc, ctx, path_ctx, e); + acc.add_enum_variants(ctx, path_ctx, e); } - let mut seen = FxHashSet::default(); - ty.iterate_path_candidates( - ctx.db, - &ctx.scope, - &ctx.traits_in_scope().0, - Some(ctx.module), - None, - |item| { - // We might iterate candidates of a trait multiple times here, so deduplicate - // them. - if seen.insert(item) { - add_assoc_item(acc, ctx, path_ctx, item); - } - None::<()> - }, - ); + + ctx.iterate_path_candidates(&ty, |item| { + add_assoc_item(acc, item); + }); } _ => (), } @@ -212,7 +185,7 @@ pub(crate) fn complete_expr_path( if is_func_update.is_none() { let mut add_keyword = - |kw, snippet| acc.add_keyword_snippet_expr(ctx, kw, snippet, incomplete_let); + |kw, snippet| acc.add_keyword_snippet_expr(ctx, incomplete_let, kw, snippet); if !in_block_expr { add_keyword("unsafe", "unsafe {\n $0\n}"); @@ -265,27 +238,3 @@ pub(crate) fn complete_expr_path( } } } - -fn add_assoc_item( - acc: &mut Completions, - ctx: &CompletionContext, - path_ctx: &PathCompletionCtx, - item: hir::AssocItem, -) { - match item { - hir::AssocItem::Function(func) => acc.add_function(ctx, path_ctx, func, None), - hir::AssocItem::Const(ct) => acc.add_const(ctx, ct), - hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), - } -} - -fn add_enum_variants( - acc: &mut Completions, - ctx: &CompletionContext, - path_ctx: &PathCompletionCtx, - e: hir::Enum, -) { - e.variants(ctx.db) - .into_iter() - .for_each(|variant| acc.add_enum_variant(ctx, path_ctx, variant, None)); -} diff --git a/crates/ide-completion/src/completions/pattern.rs b/crates/ide-completion/src/completions/pattern.rs index 4ea80a5077..7a344c2c7b 100644 --- a/crates/ide-completion/src/completions/pattern.rs +++ b/crates/ide-completion/src/completions/pattern.rs @@ -1,7 +1,6 @@ //! Completes constants and paths in unqualified patterns. use hir::{db::DefDatabase, AssocItem, ScopeDef}; -use ide_db::FxHashSet; use syntax::ast::Pat; use crate::{ @@ -81,9 +80,7 @@ pub(crate) fn complete_pattern( hir::ModuleDef::Adt(hir::Adt::Enum(e)) => refutable || single_variant_enum(e), hir::ModuleDef::Const(..) => refutable, hir::ModuleDef::Module(..) => true, - hir::ModuleDef::Macro(mac) if mac.is_fn_like(ctx.db) => { - return acc.add_macro_pat(ctx, pattern_ctx, mac, name); - } + hir::ModuleDef::Macro(mac) => mac.is_fn_like(ctx.db), _ => false, }, hir::ScopeDef::ImplSelfType(impl_) => match impl_.self_ty(ctx.db).as_adt() { @@ -136,12 +133,7 @@ pub(crate) fn complete_pattern_path( } } } - res @ (hir::PathResolution::TypeParam(_) - | hir::PathResolution::SelfType(_) - | hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Struct(_))) - | hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Enum(_))) - | hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Union(_))) - | hir::PathResolution::Def(hir::ModuleDef::BuiltinType(_))) => { + res => { let ty = match res { hir::PathResolution::TypeParam(param) => param.ty(ctx.db), hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db), @@ -149,10 +141,6 @@ pub(crate) fn complete_pattern_path( s.ty(ctx.db) } hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Enum(e))) => { - cov_mark::hit!(enum_plain_qualified_use_tree); - e.variants(ctx.db).into_iter().for_each(|variant| { - acc.add_enum_variant(ctx, path_ctx, variant, None) - }); e.ty(ctx.db) } hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Union(u))) => { @@ -162,41 +150,33 @@ pub(crate) fn complete_pattern_path( _ => return, }; - let mut seen = FxHashSet::default(); - ty.iterate_path_candidates( - ctx.db, - &ctx.scope, - &ctx.scope.visible_traits().0, - Some(ctx.module), - None, - |item| { - match item { - AssocItem::TypeAlias(ta) => { - // We might iterate candidates of a trait multiple times here, so deduplicate them. - if seen.insert(item) { - acc.add_type_alias(ctx, ta); - } - } - AssocItem::Const(c) => { - if seen.insert(item) { - acc.add_const(ctx, c); - } - } - _ => {} - } - None::<()> - }, - ); + if let Some(hir::Adt::Enum(e)) = ty.as_adt() { + cov_mark::hit!(enum_plain_qualified_use_tree); + acc.add_enum_variants(ctx, path_ctx, e); + } + + ctx.iterate_path_candidates(&ty, |item| match item { + AssocItem::TypeAlias(ta) => acc.add_type_alias(ctx, ta), + AssocItem::Const(c) => acc.add_const(ctx, c), + _ => {} + }); } - _ => {} } } - // qualifier can only be none here if we are in a TuplePat or RecordPat in which case special characters have to follow the 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| { - // FIXME: properly filter here - if let ScopeDef::ModuleDef(_) = res { + // 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), + ScopeDef::ModuleDef(hir::ModuleDef::Adt(_)) => true, + ScopeDef::ModuleDef(hir::ModuleDef::Variant(_)) => true, + ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) => true, + ScopeDef::ImplSelfType(_) => true, + _ => false, + }; + if add_completion { acc.add_path_resolution(ctx, path_ctx, name, res); } }); diff --git a/crates/ide-completion/src/completions/record.rs b/crates/ide-completion/src/completions/record.rs index 8cef3a7018..9f5922c5b0 100644 --- a/crates/ide-completion/src/completions/record.rs +++ b/crates/ide-completion/src/completions/record.rs @@ -17,6 +17,7 @@ pub(crate) fn complete_record_pattern_fields( complete_fields(acc, ctx, ctx.sema.record_pattern_missing_fields(record_pat)); } } + pub(crate) fn complete_record_expr_fields( acc: &mut Completions, ctx: &CompletionContext, @@ -41,7 +42,6 @@ pub(crate) fn complete_record_expr_fields( } _ => { let missing_fields = ctx.sema.record_literal_missing_fields(record_expr); - add_default_update(acc, ctx, ty, &missing_fields); if dot_prefix { let mut item = @@ -56,29 +56,7 @@ pub(crate) fn complete_record_expr_fields( complete_fields(acc, ctx, missing_fields); } -fn add_default_update( - acc: &mut Completions, - ctx: &CompletionContext, - ty: Option, - missing_fields: &[(hir::Field, hir::Type)], -) { - let default_trait = ctx.famous_defs().core_default_Default(); - let impl_default_trait = default_trait - .zip(ty.as_ref()) - .map_or(false, |(default_trait, ty)| ty.original.impls_trait(ctx.db, default_trait, &[])); - if impl_default_trait && !missing_fields.is_empty() { - let completion_text = "..Default::default()"; - let mut item = CompletionItem::new(SymbolKind::Field, ctx.source_range(), completion_text); - let completion_text = - completion_text.strip_prefix(ctx.token.text()).unwrap_or(completion_text); - item.insert_text(completion_text).set_relevance(CompletionRelevance { - postfix_match: Some(CompletionRelevancePostfixMatch::Exact), - ..Default::default() - }); - item.add_to(acc); - } -} - +// FIXME: This should probably be part of complete_path_expr pub(crate) fn complete_record_expr_func_update( acc: &mut Completions, ctx: &CompletionContext, @@ -101,6 +79,30 @@ pub(crate) fn complete_record_expr_func_update( } } +fn add_default_update( + acc: &mut Completions, + ctx: &CompletionContext, + ty: Option, + missing_fields: &[(hir::Field, hir::Type)], +) { + let default_trait = ctx.famous_defs().core_default_Default(); + let impl_default_trait = default_trait + .zip(ty.as_ref()) + .map_or(false, |(default_trait, ty)| ty.original.impls_trait(ctx.db, default_trait, &[])); + if impl_default_trait && !missing_fields.is_empty() { + // FIXME: This should make use of scope_def like completions so we get all the other goodies + let completion_text = "..Default::default()"; + let mut item = CompletionItem::new(SymbolKind::Field, ctx.source_range(), completion_text); + let completion_text = + completion_text.strip_prefix(ctx.token.text()).unwrap_or(completion_text); + item.insert_text(completion_text).set_relevance(CompletionRelevance { + postfix_match: Some(CompletionRelevancePostfixMatch::Exact), + ..Default::default() + }); + item.add_to(acc); + } +} + fn complete_fields( acc: &mut Completions, ctx: &CompletionContext, diff --git a/crates/ide-completion/src/completions/snippet.rs b/crates/ide-completion/src/completions/snippet.rs index 9992a81fe0..a530f4262e 100644 --- a/crates/ide-completion/src/completions/snippet.rs +++ b/crates/ide-completion/src/completions/snippet.rs @@ -9,12 +9,6 @@ use crate::{ CompletionContext, CompletionItem, CompletionItemKind, Completions, SnippetScope, }; -fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder { - let mut item = CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), label); - item.insert_snippet(cap, snippet); - item -} - pub(crate) fn complete_expr_snippet( acc: &mut Completions, ctx: &CompletionContext, @@ -124,6 +118,12 @@ macro_rules! $1 { } } +fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder { + let mut item = CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), label); + item.insert_snippet(cap, snippet); + item +} + fn add_custom_completions( acc: &mut Completions, ctx: &CompletionContext, diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs index c5b65d36ae..616d862154 100644 --- a/crates/ide-completion/src/completions/type.rs +++ b/crates/ide-completion/src/completions/type.rs @@ -1,7 +1,6 @@ //! Completion of names from the current scope in type position. use hir::{HirDisplay, ScopeDef}; -use ide_db::FxHashSet; use syntax::{ast, AstNode}; use crate::{ @@ -52,9 +51,8 @@ pub(crate) fn complete_type_path( match qualified { Qualified::Infer => ctx .traits_in_scope() - .0 - .into_iter() - .flat_map(|it| hir::Trait::from(it).items(ctx.sema.db)) + .iter() + .flat_map(|&it| hir::Trait::from(it).items(ctx.sema.db)) .for_each(|item| add_assoc_item(acc, item)), Qualified::With { resolution: None, .. } => {} Qualified::With { resolution: Some(resolution), .. } => { @@ -88,17 +86,9 @@ pub(crate) fn complete_type_path( // XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType. // (where AssocType is defined on a trait, not an inherent impl) - ty.iterate_path_candidates( - ctx.db, - &ctx.scope, - &ctx.traits_in_scope().0, - Some(ctx.module), - None, - |item| { - add_assoc_item(acc, item); - None::<()> - }, - ); + ctx.iterate_path_candidates(&ty, |item| { + add_assoc_item(acc, item); + }); // Iterate assoc types separately ty.iterate_assoc_items(ctx.db, ctx.krate, |item| { @@ -121,22 +111,9 @@ pub(crate) fn complete_type_path( _ => return, }; - let mut seen = FxHashSet::default(); - ty.iterate_path_candidates( - ctx.db, - &ctx.scope, - &ctx.traits_in_scope().0, - Some(ctx.module), - None, - |item| { - // We might iterate candidates of a trait multiple times here, so deduplicate - // them. - if seen.insert(item) { - add_assoc_item(acc, item); - } - None::<()> - }, - ); + ctx.iterate_path_candidates(&ty, |item| { + add_assoc_item(acc, item); + }); } _ => (), } diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 8ea03358ae..01a2f96fd1 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -371,7 +371,9 @@ impl<'a> CompletionContext<'a> { where I: hir::HasVisibility + hir::HasAttrs + hir::HasCrate + Copy, { - self.is_visible_impl(&item.visibility(self.db), &item.attrs(self.db), item.krate(self.db)) + let vis = item.visibility(self.db); + let attrs = item.attrs(self.db); + self.is_visible_impl(&vis, &attrs, item.krate(self.db)) } pub(crate) fn is_scope_def_hidden(&self, scope_def: ScopeDef) -> bool { @@ -391,6 +393,7 @@ impl<'a> CompletionContext<'a> { _ => false, } } + /// 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() { @@ -408,6 +411,29 @@ impl<'a> CompletionContext<'a> { traits_in_scope } + pub(crate) fn iterate_path_candidates( + &self, + ty: &hir::Type, + mut cb: impl FnMut(hir::AssocItem), + ) { + let mut seen = FxHashSet::default(); + ty.iterate_path_candidates( + self.db, + &self.scope, + &self.traits_in_scope(), + Some(self.module), + None, + |item| { + // We might iterate candidates of a trait multiple times here, so deduplicate + // them. + if seen.insert(item) { + cb(item) + } + None::<()> + }, + ); + } + /// 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)) { let _p = profile::span("CompletionContext::process_all_names"); diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs index fe02f05fd1..b806d955d9 100644 --- a/crates/ide-completion/src/lib.rs +++ b/crates/ide-completion/src/lib.rs @@ -148,7 +148,7 @@ pub fn completions( config: &CompletionConfig, position: FilePosition, trigger_character: Option, -) -> Option { +) -> Option> { let (ctx, analysis) = &CompletionContext::new(db, position, config)?; let mut completions = Completions::default(); @@ -163,7 +163,7 @@ pub fn completions( } } // prevent `(` from triggering unwanted completion noise - return Some(completions); + return Some(completions.into()); } { @@ -197,7 +197,7 @@ pub fn completions( } } - Some(completions) + Some(completions.into()) } /// Resolves additional completion data at the position given. diff --git a/crates/ide-completion/src/tests/pattern.rs b/crates/ide-completion/src/tests/pattern.rs index 8953f1b2fd..7169209d81 100644 --- a/crates/ide-completion/src/tests/pattern.rs +++ b/crates/ide-completion/src/tests/pattern.rs @@ -399,7 +399,6 @@ fn foo() { #[test] fn completes_no_delims_if_existing() { - // FIXME: We should not complete functions here check_empty( r#" struct Bar(u32); @@ -410,9 +409,7 @@ fn foo() { } "#, expect![[r#" - fn foo fn() st Bar - bt u32 kw crate:: kw self:: kw super:: @@ -428,9 +425,7 @@ fn foo() { } "#, expect![[r#" - fn foo fn() st Foo - bt u32 kw crate:: kw self:: kw super::