diff --git a/Cargo.toml b/Cargo.toml index a08b2e412e47..39a2665c003e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -181,7 +181,6 @@ hashbrown = { version = "0.14.*", features = [ ], default-features = false } [workspace.lints.rust] -# remember to update RUSTFLAGS in ci.yml if you add something here elided_lifetimes_in_paths = "warn" explicit_outlives_requirements = "warn" @@ -241,3 +240,8 @@ print_stderr = "warn" rc_buffer = "warn" str_to_string = "warn" + +[workspace.lints.rustdoc] + +# We treat the codebase as one big unit, private links are fine. +private-intra-doc-links = "allow" diff --git a/crates/hir-expand/src/mod_path.rs b/crates/hir-expand/src/mod_path.rs index 9142ad8b9211..640ca1f508b3 100644 --- a/crates/hir-expand/src/mod_path.rs +++ b/crates/hir-expand/src/mod_path.rs @@ -192,6 +192,15 @@ impl ModPath { ) -> impl fmt::Display + 'a { Display { db, path: self, edition: Some(edition) } } + + pub fn last_name(&self) -> Name { + self.segments.last().cloned().unwrap_or_else(|| match self.kind { + PathKind::DollarCrate(_) | PathKind::Abs | PathKind::Plain => Name::missing(), + PathKind::SELF => Name::new_symbol_root(sym::self_), + PathKind::Super(_) => Name::new_symbol_root(sym::super_), + PathKind::Crate => Name::new_symbol_root(sym::crate_), + }) + } } impl Extend for ModPath { diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs index 9a61885ccb70..31ece942806c 100644 --- a/crates/hir/src/attrs.rs +++ b/crates/hir/src/attrs.rs @@ -110,11 +110,42 @@ impl AttrsWithOwner { self.attrs.contains(AttrFlags::IS_DOC_NOTABLE_TRAIT) } + /// Never call this for macros. #[inline] - pub fn is_doc_hidden(&self) -> bool { + pub(crate) fn is_doc_hidden_raw(&self) -> bool { self.attrs.contains(AttrFlags::IS_DOC_HIDDEN) } + /// This checks whether this item is `#[doc(hidden)]`. However macros are special in this regard: + /// we want to treat usages of non-hidden reexports of hidden macros as non-hidden, see [`Macro::legacy_macros_reexport()`]. + /// We don't have a real semantic way to distinguish between them, so we use the name as an indication. + pub fn is_doc_hidden( + &self, + db: &dyn HirDatabase, + under_name: &Name, + in_module: Module, + ) -> bool { + if !self.is_doc_hidden_raw() { + return false; + } + + if let AttrsOwner::AttrDef(AttrDefId::MacroId(makro)) = self.owner + && let Some((reexport_name, reexport)) = + (Macro { id: makro }).legacy_macros_reexport(db) + && *under_name == reexport_name + && reexport.vis.is_visible_from(db, in_module.id) + { + // Two things are noticeable here: + // 1. We don't check whether the reexport has `#[doc(hidden)]`, because we have no easy and performant way + // to check attributes of reexports. + // 2. If the reexport has the same name as the macro, we always treat it as non-hidden. We do so because + // it's common to use the same name. + return false; + } + + true + } + #[inline] pub fn is_deprecated(&self) -> bool { self.attrs.contains(AttrFlags::IS_DEPRECATED) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index d187763151a2..bd54b6da9779 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -1694,6 +1694,10 @@ impl EnumVariant { pub fn is_unstable(self, db: &dyn HirDatabase) -> bool { AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_UNSTABLE) } + + pub fn is_doc_hidden(self, db: &dyn HirDatabase) -> bool { + AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_DOC_HIDDEN) + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -3247,6 +3251,10 @@ impl Macro { as_name_opt(source.value.name()) } MacroId::MacroRulesId(id) => { + if let Some((reexport_name, _)) = self.legacy_macros_reexport(db) { + return reexport_name; + } + let loc = id.lookup(db); let source = loc.source(db); as_name_opt(source.value.name()) @@ -3357,6 +3365,50 @@ impl Macro { let attrs = self.attrs(db); MacroBraces::extract(attrs.attrs) } + + // Feature: Detection of Exported `macro_rules!` + // + // `macro_rules!` macros can only be publicly exported via `#[macro_export]` and at the crate root. + // For proper scoping, a common practice is to mark the macro `#[doc(hidden)]`, then have a public reexport + // where you want to export the macro. rust-analyzer generally supports this practice and detects the correct + // way to call the macro, however as a crate author you must make sure two conditions are fulfilled: + // + // 1. The reexport is *exactly in the same module* as the macro. If this condition is not fulfilled, + // rust-analyzer will fail to detect the reexport and consider the macro hidden. Of course, you can then + // reexport the reexport wherever you like. + // 2. For best experience, the reexport should be renaming. That is, declare the macro with a different name + // than you want (a common practice is to call it `__`) then rename it in the reexport, like + // `pub use __my_macro as my_macro`. If you do not do this, rust-analyzer will still find the reexport, + // but it will also consider the macro not hidden at the crate root. + + /// Legacy (`macro_rules!`) macros cannot have proper namespacing resolution in Rust (more precisely, they + /// cannot have it across crates). Therefore, it is a common practice to make a macro `#[doc(hidden)]` + /// and put `pub use __macro as macro` in the same module. In such cases, this function will return + /// the reexport. + fn legacy_macros_reexport( + &self, + db: &dyn HirDatabase, + ) -> Option<(Name, hir_def::per_ns::MacrosItem)> { + let MacroId::MacroRulesId(makro) = self.id else { + return None; + }; + if !AttrFlags::query(db, makro.into()) + .contains(AttrFlags::IS_DOC_HIDDEN | AttrFlags::IS_MACRO_EXPORT) + { + return None; + } + let loc = makro.loc(db); + let def_map = loc.container.def_map(db); + if loc.container == def_map.crate_root(db) { + // `#[macro_export]` macros defined at the crate root cannot have a reexport, + // it'll cause conflicts with the builtin export. + return None; + } + let scope = &def_map[loc.container].scope; + let (reexport_name, reexport) = + scope.macros().find(|(_, reexport)| reexport.def == self.id)?; + Some((reexport_name.clone(), reexport)) + } } // Feature: Macro Brace Style Attribute @@ -3769,6 +3821,10 @@ impl AssocItem { } } } + + pub fn is_doc_hidden(&self, db: &dyn HirDatabase) -> bool { + self.attrs(db).is_doc_hidden_raw() + } } impl HasVisibility for AssocItem { diff --git a/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/crates/ide-assists/src/handlers/add_missing_match_arms.rs index 632fe0d72cfa..f56140c6e387 100644 --- a/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/crates/ide-assists/src/handlers/add_missing_match_arms.rs @@ -498,7 +498,7 @@ impl ExtendedVariant { fn should_be_hidden(self, db: &RootDatabase, krate: Crate) -> bool { match self { ExtendedVariant::Variant { variant: var, .. } => { - var.attrs(db).is_doc_hidden() && var.module(db).krate(db) != krate + var.is_doc_hidden(db) && var.module(db).krate(db) != krate } _ => false, } diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs index 33e0f476da5b..002ac35cb22d 100644 --- a/crates/ide-assists/src/utils.rs +++ b/crates/ide-assists/src/utils.rs @@ -4,7 +4,7 @@ use std::slice; pub(crate) use gen_trait_fn_body::gen_trait_fn_body; use hir::{ - HasAttrs as HirHasAttrs, HirDisplay, InFile, ModuleDef, PathResolution, Semantics, + HirDisplay, InFile, ModuleDef, PathResolution, Semantics, db::{ExpandDatabase, HirDatabase}, }; use ide_db::{ @@ -151,7 +151,7 @@ pub fn filter_assoc_items( .copied() .filter(|assoc_item| { if ignore_items == IgnoreAssocItems::DocHiddenAttrPresent - && assoc_item.attrs(sema.db).is_doc_hidden() + && assoc_item.is_doc_hidden(sema.db) { if let hir::AssocItem::Function(f) = assoc_item && !f.has_body(sema.db) diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index 20048ea97b2c..c5e1237c73a9 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -230,7 +230,7 @@ impl Completions { resolution: hir::ScopeDef, doc_aliases: Vec, ) { - let is_private_editable = match ctx.def_is_visible(&resolution) { + let is_private_editable = match ctx.def_is_visible(&resolution, &local_name) { Visible::Yes => false, Visible::Editable => true, Visible::No => return, @@ -251,7 +251,7 @@ impl Completions { local_name: hir::Name, resolution: hir::ScopeDef, ) { - let is_private_editable = match ctx.def_is_visible(&resolution) { + let is_private_editable = match ctx.def_is_visible(&resolution, &local_name) { Visible::Yes => false, Visible::Editable => true, Visible::No => return, @@ -300,7 +300,7 @@ impl Completions { mac: hir::Macro, local_name: hir::Name, ) { - let is_private_editable = match ctx.is_visible(&mac) { + let is_private_editable = match ctx.is_visible(&mac, &local_name) { Visible::Yes => false, Visible::Editable => true, Visible::No => return, @@ -321,7 +321,7 @@ impl Completions { func: hir::Function, local_name: Option, ) { - let is_private_editable = match ctx.is_visible(&func) { + let is_private_editable = match ctx.is_visible(&func, &Name::missing()) { Visible::Yes => false, Visible::Editable => true, Visible::No => return, @@ -342,9 +342,8 @@ impl Completions { dot_access: &DotAccess<'_>, func: hir::Function, receiver: Option, - local_name: Option, ) { - let is_private_editable = match ctx.is_visible(&func) { + let is_private_editable = match ctx.is_visible(&func, &Name::missing()) { Visible::Yes => false, Visible::Editable => true, Visible::No => return, @@ -354,7 +353,6 @@ impl Completions { RenderContext::new(ctx).private_editable(is_private_editable).doc_aliases(doc_aliases), dot_access, receiver, - local_name, func, ) .add_to(self, ctx.db); @@ -367,7 +365,7 @@ impl Completions { func: hir::Function, import: LocatedImport, ) { - let is_private_editable = match ctx.is_visible(&func) { + let is_private_editable = match ctx.is_visible(&func, &Name::missing()) { Visible::Yes => false, Visible::Editable => true, Visible::No => return, @@ -380,14 +378,13 @@ impl Completions { .import_to_add(Some(import)), dot_access, None, - None, func, ) .add_to(self, ctx.db); } pub(crate) fn add_const(&mut self, ctx: &CompletionContext<'_, '_>, konst: hir::Const) { - let is_private_editable = match ctx.is_visible(&konst) { + let is_private_editable = match ctx.is_visible(&konst, &Name::missing()) { Visible::Yes => false, Visible::Editable => true, Visible::No => return, @@ -403,7 +400,7 @@ impl Completions { ctx: &CompletionContext<'_, '_>, type_alias: hir::TypeAlias, ) { - let is_private_editable = match ctx.is_visible(&type_alias) { + let is_private_editable = match ctx.is_visible(&type_alias, &Name::missing()) { Visible::Yes => false, Visible::Editable => true, Visible::No => return, @@ -432,7 +429,7 @@ impl Completions { variant: hir::EnumVariant, path: hir::ModPath, ) { - if !ctx.check_stability_and_hidden(variant) { + if !ctx.check_stability_and_hidden(variant, &Name::missing()) { return; } if let Some(builder) = @@ -449,7 +446,7 @@ impl Completions { variant: hir::EnumVariant, local_name: Option, ) { - if !ctx.check_stability_and_hidden(variant) { + if !ctx.check_stability_and_hidden(variant, &Name::missing()) { return; } if let PathCompletionCtx { kind: PathKind::Pat { pat_ctx }, .. } = path_ctx { @@ -473,7 +470,7 @@ impl Completions { field: hir::Field, ty: &hir::Type<'_>, ) { - let is_private_editable = match ctx.is_visible(&field) { + let is_private_editable = match ctx.is_visible(&field, &Name::missing()) { Visible::Yes => false, Visible::Editable => true, Visible::No => return, @@ -497,7 +494,7 @@ impl Completions { path: Option, local_name: Option, ) { - let is_private_editable = match ctx.is_visible(&strukt) { + let is_private_editable = match ctx.is_visible(&strukt, &Name::missing()) { Visible::Yes => false, Visible::Editable => true, Visible::No => return, @@ -520,7 +517,7 @@ impl Completions { path: Option, local_name: Option, ) { - let is_private_editable = match ctx.is_visible(&un) { + let is_private_editable = match ctx.is_visible(&un, &Name::missing()) { Visible::Yes => false, Visible::Editable => true, Visible::No => return, @@ -575,7 +572,7 @@ impl Completions { variant: hir::EnumVariant, local_name: Option, ) { - if !ctx.check_stability_and_hidden(variant) { + if !ctx.check_stability_and_hidden(variant, &Name::missing()) { return; } self.add_opt(render_variant_pat( @@ -595,7 +592,7 @@ impl Completions { variant: hir::EnumVariant, path: hir::ModPath, ) { - if !ctx.check_stability_and_hidden(variant) { + if !ctx.check_stability_and_hidden(variant, &Name::missing()) { return; } let path = Some(&path); @@ -616,7 +613,7 @@ impl Completions { strukt: hir::Struct, local_name: Option, ) { - let is_private_editable = match ctx.is_visible(&strukt) { + let is_private_editable = match ctx.is_visible(&strukt, &Name::missing()) { Visible::Yes => false, Visible::Editable => true, Visible::No => return, diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs index 59c6c55c22b9..bfe6699ea61c 100644 --- a/crates/ide-completion/src/completions/dot.rs +++ b/crates/ide-completion/src/completions/dot.rs @@ -69,7 +69,7 @@ pub(crate) fn complete_dot( has_parens, ); complete_methods(ctx, &future_output, &traits_in_scope, |func| { - acc.add_method(ctx, &dot_access, func, Some(await_str.clone()), None) + acc.add_method(ctx, &dot_access, func, Some(await_str.clone())) }); } } @@ -83,7 +83,7 @@ pub(crate) fn complete_dot( has_parens, ); complete_methods(ctx, receiver_ty, &traits_in_scope, |func| { - acc.add_method(ctx, dot_access, func, None, None) + acc.add_method(ctx, dot_access, func, None) }); if ctx.config.enable_auto_iter && !receiver_ty.strip_references().impls_iterator(ctx.db) { @@ -118,7 +118,7 @@ pub(crate) fn complete_dot( ctx: dot_access.ctx, }; complete_methods(ctx, &iter, &traits_in_scope, |func| { - acc.add_method(ctx, &dot_access, func, Some(iter_sym.clone()), None) + acc.add_method(ctx, &dot_access, func, Some(iter_sym.clone())) }); } } @@ -191,7 +191,6 @@ pub(crate) fn complete_undotted_self( }, func, Some(SmolStr::new_static(param_name)), - None, ) }); } @@ -256,7 +255,7 @@ fn complete_methods( let do_complete = match &same_name { hash_map::Entry::Vacant(_) => true, hash_map::Entry::Occupied(same_func) => { - match self.ctx.is_visible(same_func.get()) { + match self.ctx.is_visible(same_func.get(), same_func.key()) { crate::context::Visible::Yes => false, crate::context::Visible::Editable => true, crate::context::Visible::No => true, diff --git a/crates/ide-completion/src/completions/flyimport.rs b/crates/ide-completion/src/completions/flyimport.rs index b350647b9a2b..a3b822f27937 100644 --- a/crates/ide-completion/src/completions/flyimport.rs +++ b/crates/ide-completion/src/completions/flyimport.rs @@ -270,8 +270,9 @@ fn import_on_the_fly<'db>( .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) + let name = import.import_path.last_name(); + !ctx.is_item_hidden(&import.item_to_import, &name) + && !ctx.is_item_hidden(original_item, &name) && ctx.check_stability(original_item.attrs(ctx.db).as_ref()) }) .filter(|import| filter_excluded_flyimport(ctx, import)) @@ -317,8 +318,9 @@ fn import_on_the_fly_pat_<'db>( .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) + let name = import.import_path.last_name(); + !ctx.is_item_hidden(&import.item_to_import, &name) + && !ctx.is_item_hidden(original_item, &name) && ctx.check_stability(original_item.attrs(ctx.db).as_ref()) }) .sorted_by(|a, b| { @@ -357,8 +359,9 @@ fn import_on_the_fly_method<'db>( import_assets .search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind) .filter(|import| { - !ctx.is_item_hidden(&import.item_to_import) - && !ctx.is_item_hidden(&import.original_item) + let name = import.import_path.last_name(); + !ctx.is_item_hidden(&import.item_to_import, &name) + && !ctx.is_item_hidden(&import.original_item, &name) }) .filter(|import| filter_excluded_flyimport(ctx, import)) .sorted_by(|a, b| { diff --git a/crates/ide-completion/src/completions/item_list/trait_impl.rs b/crates/ide-completion/src/completions/item_list/trait_impl.rs index c165a32082df..5f1195c2a58e 100644 --- a/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -161,7 +161,7 @@ fn complete_trait_impl( if let Some(hir_impl) = ctx.sema.to_def(impl_def) { get_missing_assoc_items(&ctx.sema, impl_def) .into_iter() - .filter(|item| ctx.check_stability_and_hidden(*item)) + .filter(|item| ctx.check_stability_and_hidden(*item, &Name::missing())) .for_each(|item| { use self::ImplCompletionKind::*; match (item, kind) { diff --git a/crates/ide-completion/src/completions/use_.rs b/crates/ide-completion/src/completions/use_.rs index 1ff7dd6deff0..6bc8c4e03ca0 100644 --- a/crates/ide-completion/src/completions/use_.rs +++ b/crates/ide-completion/src/completions/use_.rs @@ -55,7 +55,7 @@ pub(crate) fn complete_use_path( if let (Some(attrs), Some(defining_crate)) = (def.attrs(ctx.db), def.krate(ctx.db)) && (!ctx.check_stability(Some(&attrs)) - || ctx.is_doc_hidden(&attrs, defining_crate)) + || ctx.is_doc_hidden(&attrs, &name, defining_crate)) { continue; } diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index df6282926936..ca356e448077 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -545,18 +545,18 @@ impl<'db> CompletionContext<'_, 'db> { } /// Checks if an item is visible and not `doc(hidden)` at the completion site. - pub(crate) fn def_is_visible(&self, item: &ScopeDef) -> Visible { + pub(crate) fn def_is_visible(&self, item: &ScopeDef, under_name: &Name) -> Visible { match item { ScopeDef::ModuleDef(def) => match def { - hir::ModuleDef::Module(it) => self.is_visible(it), - hir::ModuleDef::Function(it) => self.is_visible(it), - hir::ModuleDef::Adt(it) => self.is_visible(it), - hir::ModuleDef::EnumVariant(it) => self.is_visible(it), - hir::ModuleDef::Const(it) => self.is_visible(it), - hir::ModuleDef::Static(it) => self.is_visible(it), - hir::ModuleDef::Trait(it) => self.is_visible(it), - hir::ModuleDef::TypeAlias(it) => self.is_visible(it), - hir::ModuleDef::Macro(it) => self.is_visible(it), + hir::ModuleDef::Module(it) => self.is_visible(it, under_name), + hir::ModuleDef::Function(it) => self.is_visible(it, under_name), + hir::ModuleDef::Adt(it) => self.is_visible(it, under_name), + hir::ModuleDef::EnumVariant(it) => self.is_visible(it, under_name), + hir::ModuleDef::Const(it) => self.is_visible(it, under_name), + hir::ModuleDef::Static(it) => self.is_visible(it, under_name), + hir::ModuleDef::Trait(it) => self.is_visible(it, under_name), + hir::ModuleDef::TypeAlias(it) => self.is_visible(it, under_name), + hir::ModuleDef::Macro(it) => self.is_visible(it, under_name), hir::ModuleDef::BuiltinType(_) => Visible::Yes, }, ScopeDef::GenericParam(_) @@ -569,13 +569,13 @@ impl<'db> CompletionContext<'_, 'db> { } /// Checks if an item is visible, not `doc(hidden)` and stable at the completion site. - pub(crate) fn is_visible(&self, item: &I) -> Visible + pub(crate) fn is_visible(&self, item: &I, under_name: &Name) -> Visible where I: hir::HasVisibility + hir::HasAttrs + hir::HasCrate + Copy, { let vis = item.visibility(self.db); let attrs = item.attrs(self.db); - self.is_visible_impl(&vis, &attrs, item.krate(self.db)) + self.is_visible_impl(&vis, &attrs, under_name, item.krate(self.db)) } pub(crate) fn doc_aliases(&self, item: &I) -> Vec @@ -587,11 +587,11 @@ impl<'db> CompletionContext<'_, 'db> { } /// Check if an item is `#[doc(hidden)]`. - pub(crate) fn is_item_hidden(&self, item: &hir::ItemInNs) -> bool { + pub(crate) fn is_item_hidden(&self, item: &hir::ItemInNs, under_name: &Name) -> bool { let attrs = item.attrs(self.db); let krate = item.krate(self.db); match (attrs, krate) { - (Some(attrs), Some(krate)) => self.is_doc_hidden(&attrs, krate), + (Some(attrs), Some(krate)) => self.is_doc_hidden(&attrs, under_name, krate), _ => false, } } @@ -615,13 +615,14 @@ impl<'db> CompletionContext<'_, 'db> { || self.krate.is_unstable_feature_enabled(self.db, &unstable_feature) } - pub(crate) fn check_stability_and_hidden(&self, item: I) -> bool + pub(crate) fn check_stability_and_hidden(&self, item: I, under_name: &Name) -> bool where I: hir::HasAttrs + hir::HasCrate, { let defining_crate = item.krate(self.db); let attrs = item.attrs(self.db); - self.check_stability(Some(&attrs)) && !self.is_doc_hidden(&attrs, defining_crate) + self.check_stability(Some(&attrs)) + && !self.is_doc_hidden(&attrs, under_name, defining_crate) } /// Whether the given trait is an operator trait or not. @@ -667,7 +668,7 @@ impl<'db> CompletionContext<'_, 'db> { pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef, Vec)) { let _p = tracing::info_span!("CompletionContext::process_all_names").entered(); self.scope.process_all_names(&mut |name, def| { - if self.is_scope_def_hidden(def) { + if self.is_scope_def_hidden(def, &name) { return; } let doc_aliases = self.doc_aliases_in_scope(def); @@ -680,9 +681,9 @@ impl<'db> CompletionContext<'_, 'db> { self.scope.process_all_names(f); } - fn is_scope_def_hidden(&self, scope_def: ScopeDef) -> bool { + fn is_scope_def_hidden(&self, scope_def: ScopeDef, name: &Name) -> bool { if let (Some(attrs), Some(krate)) = (scope_def.attrs(self.db), scope_def.krate(self.db)) { - return self.is_doc_hidden(&attrs, krate); + return self.is_doc_hidden(&attrs, name, krate); } false @@ -692,6 +693,7 @@ impl<'db> CompletionContext<'_, 'db> { &self, vis: &hir::Visibility, attrs: &hir::AttrsWithOwner, + under_name: &Name, defining_crate: hir::Crate, ) -> Visible { if !self.check_stability(Some(attrs)) { @@ -710,16 +712,21 @@ impl<'db> CompletionContext<'_, 'db> { }; } - if self.is_doc_hidden(attrs, defining_crate) { Visible::No } else { Visible::Yes } + if self.is_doc_hidden(attrs, under_name, defining_crate) { + Visible::No + } else { + Visible::Yes + } } pub(crate) fn is_doc_hidden( &self, attrs: &hir::AttrsWithOwner, + under_name: &Name, defining_crate: hir::Crate, ) -> bool { // `doc(hidden)` items are only completed within the defining crate. - self.krate != defining_crate && attrs.is_doc_hidden() + self.krate != defining_crate && attrs.is_doc_hidden(self.db, under_name, self.module) } pub(crate) fn doc_aliases_in_scope(&self, scope_def: ScopeDef) -> Vec { diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs index 4f70a90affbd..0f549b0308da 100644 --- a/crates/ide-completion/src/render/function.rs +++ b/crates/ide-completion/src/render/function.rs @@ -40,11 +40,10 @@ pub(crate) fn render_method( ctx: RenderContext<'_, '_>, dot_access: &DotAccess<'_>, receiver: Option, - local_name: Option, func: hir::Function, ) -> Builder { let _p = tracing::info_span!("render_method").entered(); - render(ctx, local_name, func, FuncKind::Method(dot_access, receiver)) + render(ctx, None, func, FuncKind::Method(dot_access, receiver)) } fn render( diff --git a/crates/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs index 45db8ecfc6a5..35f36f5d9928 100644 --- a/crates/ide-completion/src/tests/flyimport.rs +++ b/crates/ide-completion/src/tests/flyimport.rs @@ -2090,3 +2090,26 @@ fn baz() { "#, ); } + +#[test] +fn reexport_of_doc_hidden_legacy_macro() { + check( + r#" +//- /foo.rs crate:foo +pub mod bar { + #[macro_export] + #[doc(hidden)] + macro_rules! __my_macro { + () => {}; + } + pub use __my_macro as my_macro; +} + +//- /bar.rs crate:bar deps:foo +my_macro$0! {} + "#, + expect![[r#" + ma my_macro (use foo::bar::my_macro) macro_rules! my_macro + "#]], + ); +} diff --git a/crates/ide-completion/src/tests/item.rs b/crates/ide-completion/src/tests/item.rs index bb79af7e98df..451ebb3ca073 100644 --- a/crates/ide-completion/src/tests/item.rs +++ b/crates/ide-completion/src/tests/item.rs @@ -462,3 +462,26 @@ fn main() { "#]], ); } + +#[test] +fn reexport_of_doc_hidden_legacy_macro() { + check( + r#" +//- /foo.rs crate:foo +pub mod bar { + #[macro_export] + #[doc(hidden)] + macro_rules! __my_macro { + () => {}; + } + pub use __my_macro as my_macro; +} + +//- /bar.rs crate:bar deps:foo +use foo::bar::$0; + "#, + expect![[r#" + ma my_macro macro_rules! my_macro + "#]], + ); +} diff --git a/xtask/src/codegen/feature_docs.rs b/xtask/src/codegen/feature_docs.rs index 170de5db9a7a..bc843d85c7d6 100644 --- a/xtask/src/codegen/feature_docs.rs +++ b/xtask/src/codegen/feature_docs.rs @@ -58,20 +58,27 @@ impl Feature { } fn is_valid_feature_name(feature: &str) -> Result<(), String> { + let mut in_code = false; 'word: for word in feature.split_whitespace() { - for short in ["to", "and"] { + for short in ["to", "and", "of"] { if word == short { continue 'word; } } - for short in ["To", "And"] { + for short in ["To", "And", "Of"] { if word == short { return Err(format!("Don't capitalize {word:?}")); } } - if !word.starts_with(char::is_uppercase) { + if word.starts_with('`') { + in_code = true; + } + if !in_code && !word.starts_with(char::is_uppercase) { return Err(format!("Capitalize {word:?}")); } + if word.ends_with('`') { + in_code = false; + } } Ok(()) }