From 0fbf396f0dfaeaf661a647ff6dc3b405d44418e3 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Wed, 28 Jul 2021 15:59:02 +0200 Subject: [PATCH] Make most completions respect `#[doc(hidden)]` --- crates/hir/src/lib.rs | 40 ++++++++++++ .../src/completions/attribute/derive.rs | 2 +- .../ide_completion/src/completions/pattern.rs | 2 +- .../src/completions/qualified_path.rs | 36 ++++++++++- .../src/completions/unqualified_path.rs | 62 +++++++++++++++++-- crates/ide_completion/src/context.rs | 33 +++++++++- 6 files changed, 165 insertions(+), 10 deletions(-) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 9673885e73..162ff55471 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -351,6 +351,20 @@ impl ModuleDef { } acc } + + pub fn attrs(&self, db: &dyn HirDatabase) -> Option { + Some(match self { + ModuleDef::Module(it) => it.attrs(db), + ModuleDef::Function(it) => it.attrs(db), + ModuleDef::Adt(it) => it.attrs(db), + ModuleDef::Variant(it) => it.attrs(db), + ModuleDef::Const(it) => it.attrs(db), + ModuleDef::Static(it) => it.attrs(db), + ModuleDef::Trait(it) => it.attrs(db), + ModuleDef::TypeAlias(it) => it.attrs(db), + ModuleDef::BuiltinType(_) => return None, + }) + } } impl HasVisibility for ModuleDef { @@ -2725,6 +2739,32 @@ impl ScopeDef { items } + + pub fn attrs(&self, db: &dyn HirDatabase) -> Option { + match self { + ScopeDef::ModuleDef(it) => it.attrs(db), + ScopeDef::MacroDef(it) => Some(it.attrs(db)), + ScopeDef::GenericParam(it) => Some(it.attrs(db)), + ScopeDef::ImplSelfType(_) + | ScopeDef::AdtSelfType(_) + | ScopeDef::Local(_) + | ScopeDef::Label(_) + | ScopeDef::Unknown => None, + } + } + + pub fn krate(&self, db: &dyn HirDatabase) -> Option { + match self { + ScopeDef::ModuleDef(it) => it.module(db).map(|m| m.krate()), + ScopeDef::MacroDef(it) => it.module(db).map(|m| m.krate()), + ScopeDef::GenericParam(it) => Some(it.module(db).krate()), + ScopeDef::ImplSelfType(_) => None, + ScopeDef::AdtSelfType(it) => Some(it.module(db).krate()), + ScopeDef::Local(it) => Some(it.module(db).krate()), + ScopeDef::Label(it) => Some(it.module(db).krate()), + ScopeDef::Unknown => None, + } + } } impl From for ScopeDef { diff --git a/crates/ide_completion/src/completions/attribute/derive.rs b/crates/ide_completion/src/completions/attribute/derive.rs index f482d44646..b53824aec6 100644 --- a/crates/ide_completion/src/completions/attribute/derive.rs +++ b/crates/ide_completion/src/completions/attribute/derive.rs @@ -54,7 +54,7 @@ fn get_derive_names_in_scope( ctx: &CompletionContext, ) -> FxHashMap> { let mut result = FxHashMap::default(); - ctx.scope.process_all_names(&mut |name, scope_def| { + ctx.process_all_names(&mut |name, scope_def| { if let hir::ScopeDef::MacroDef(mac) = scope_def { if mac.kind() == hir::MacroKind::Derive { result.insert(name.to_string(), mac.docs(ctx.db)); diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs index bd13a62d75..f892a31505 100644 --- a/crates/ide_completion/src/completions/pattern.rs +++ b/crates/ide_completion/src/completions/pattern.rs @@ -22,7 +22,7 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { // FIXME: ideally, we should look at the type we are matching against and // suggest variants + auto-imports - ctx.scope.process_all_names(&mut |name, res| { + ctx.process_all_names(&mut |name, res| { let add_resolution = match &res { hir::ScopeDef::ModuleDef(def) => match def { hir::ModuleDef::Adt(hir::Adt::Struct(strukt)) => { diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs index 1647ecb99a..970f7db559 100644 --- a/crates/ide_completion/src/completions/qualified_path.rs +++ b/crates/ide_completion/src/completions/qualified_path.rs @@ -102,6 +102,11 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon } } + if ctx.is_scope_def_hidden(&def) { + cov_mark::hit!(qualified_path_doc_hidden); + continue; + } + let add_resolution = match def { // Don't suggest attribute macros and derives. hir::ScopeDef::MacroDef(mac) => mac.is_fn_like(), @@ -119,7 +124,6 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon _ => true, }; - // FIXME: respect #[doc(hidden)] (see `CompletionContext::is_visible`) if add_resolution { acc.add_resolution(ctx, name, &def); } @@ -666,4 +670,34 @@ fn main() { "#]], ); } + + #[test] + fn respects_doc_hidden() { + cov_mark::check!(qualified_path_doc_hidden); + check( + r#" +//- /lib.rs crate:lib deps:dep +fn f() { + dep::$0 +} + +//- /dep.rs crate:dep +#[doc(hidden)] +#[macro_export] +macro_rules! m { + () => {} +} + +#[doc(hidden)] +pub fn f() {} + +#[doc(hidden)] +pub struct S; + +#[doc(hidden)] +pub mod m {} + "#, + expect![[r#""#]], + ) + } } diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs index 8258026182..5d0eee08ee 100644 --- a/crates/ide_completion/src/completions/unqualified_path.rs +++ b/crates/ide_completion/src/completions/unqualified_path.rs @@ -13,7 +13,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC if ctx.in_use_tree() { // only show modules in a fresh UseTree cov_mark::hit!(unqualified_path_only_modules_in_import); - ctx.scope.process_all_names(&mut |name, res| { + ctx.process_all_names(&mut |name, res| { if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res { acc.add_resolution(ctx, name, &res); } @@ -29,7 +29,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC Some(ImmediateLocation::Visibility(_)) => return, Some(ImmediateLocation::ItemList | ImmediateLocation::Trait | ImmediateLocation::Impl) => { // only show macros in {Assoc}ItemList - ctx.scope.process_all_names(&mut |name, res| { + ctx.process_all_names(&mut |name, res| { if let hir::ScopeDef::MacroDef(mac) = res { if mac.is_fn_like() { acc.add_macro(ctx, Some(name.clone()), mac); @@ -42,7 +42,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC return; } Some(ImmediateLocation::TypeBound) => { - ctx.scope.process_all_names(&mut |name, res| { + ctx.process_all_names(&mut |name, res| { let add_resolution = match res { ScopeDef::MacroDef(mac) => mac.is_fn_like(), ScopeDef::ModuleDef(hir::ModuleDef::Trait(_) | hir::ModuleDef::Module(_)) => { @@ -83,7 +83,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC } } - ctx.scope.process_all_names(&mut |name, res| { + ctx.process_all_names(&mut |name, res| { if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) | ScopeDef::Label(_) = res { @@ -252,4 +252,58 @@ pub mod prelude { "#]], ); } + + #[test] + fn respects_doc_hidden() { + check( + r#" +//- /lib.rs crate:lib deps:std +fn f() { + format_$0 +} + +//- /std.rs crate:std +#[doc(hidden)] +#[macro_export] +macro_rules! format_args_nl { + () => {} +} + +pub mod prelude { + pub mod rust_2018 {} +} + "#, + expect![[r#" + fn f() fn() + md std + "#]], + ); + } + + #[test] + fn respects_doc_hidden_in_assoc_item_list() { + check( + r#" +//- /lib.rs crate:lib deps:std +struct S; +impl S { + format_$0 +} + +//- /std.rs crate:std +#[doc(hidden)] +#[macro_export] +macro_rules! format_args_nl { + () => {} +} + +pub mod prelude { + pub mod rust_2018 {} +} + "#, + expect![[r#" + md std + "#]], + ); + } } diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index 09694abd79..e4455b6941 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs @@ -1,7 +1,7 @@ //! See `CompletionContext` structure. use base_db::SourceDatabaseExt; -use hir::{Local, ScopeDef, Semantics, SemanticsScope, Type}; +use hir::{Local, Name, ScopeDef, Semantics, SemanticsScope, Type}; use ide_db::{ base_db::{FilePosition, SourceDatabase}, call_info::ActiveParameter, @@ -370,6 +370,25 @@ impl<'a> CompletionContext<'a> { self.is_visible_impl(&item.visibility(self.db), &item.attrs(self.db), item.krate(self.db)) } + pub(crate) fn is_scope_def_hidden(&self, scope_def: &ScopeDef) -> bool { + if let (Some(attrs), Some(krate)) = (scope_def.attrs(self.db), scope_def.krate(self.db)) { + return self.is_doc_hidden(&attrs, krate); + } + + false + } + + /// 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)) { + self.scope.process_all_names(&mut |name, def| { + if self.is_scope_def_hidden(&def) { + return; + } + + f(name, def); + }) + } + fn is_visible_impl( &self, vis: &hir::Visibility, @@ -388,12 +407,20 @@ impl<'a> CompletionContext<'a> { return is_editable; } + !self.is_doc_hidden(attrs, defining_crate) + } + + fn is_doc_hidden(&self, attrs: &hir::Attrs, defining_crate: hir::Crate) -> bool { + let module = match self.scope.module() { + Some(it) => it, + None => return true, + }; if module.krate() != defining_crate && attrs.has_doc_hidden() { // `doc(hidden)` items are only completed within the defining crate. - return false; + return true; } - true + false } fn fill_impl_def(&mut self) {