Skip to content

Add Item::from_def_id_and_kind to reduce duplication in rustdoc #77820

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Nov 18, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 26 additions & 17 deletions compiler/rustc_middle/src/hir/map/mod.rs
Original file line number Diff line number Diff line change
@@ -806,25 +806,34 @@ impl<'hir> Map<'hir> {
/// Given a node ID, gets a list of attributes associated with the AST
/// corresponding to the node-ID.
pub fn attrs(&self, id: HirId) -> &'hir [ast::Attribute] {
let attrs = match self.find_entry(id).map(|entry| entry.node) {
Some(Node::Param(a)) => Some(&a.attrs[..]),
Some(Node::Local(l)) => Some(&l.attrs[..]),
Some(Node::Item(i)) => Some(&i.attrs[..]),
Some(Node::ForeignItem(fi)) => Some(&fi.attrs[..]),
Some(Node::TraitItem(ref ti)) => Some(&ti.attrs[..]),
Some(Node::ImplItem(ref ii)) => Some(&ii.attrs[..]),
Some(Node::Variant(ref v)) => Some(&v.attrs[..]),
Some(Node::Field(ref f)) => Some(&f.attrs[..]),
Some(Node::Expr(ref e)) => Some(&*e.attrs),
Some(Node::Stmt(ref s)) => Some(s.kind.attrs(|id| self.item(id.id))),
Some(Node::Arm(ref a)) => Some(&*a.attrs),
Some(Node::GenericParam(param)) => Some(&param.attrs[..]),
let attrs = self.find_entry(id).map(|entry| match entry.node {
Node::Param(a) => &a.attrs[..],
Node::Local(l) => &l.attrs[..],
Node::Item(i) => &i.attrs[..],
Node::ForeignItem(fi) => &fi.attrs[..],
Node::TraitItem(ref ti) => &ti.attrs[..],
Node::ImplItem(ref ii) => &ii.attrs[..],
Node::Variant(ref v) => &v.attrs[..],
Node::Field(ref f) => &f.attrs[..],
Node::Expr(ref e) => &*e.attrs,
Node::Stmt(ref s) => s.kind.attrs(|id| self.item(id.id)),
Node::Arm(ref a) => &*a.attrs,
Node::GenericParam(param) => &param.attrs[..],
// Unit/tuple structs/variants take the attributes straight from
// the struct/variant definition.
Some(Node::Ctor(..)) => return self.attrs(self.get_parent_item(id)),
Some(Node::Crate(item)) => Some(&item.attrs[..]),
_ => None,
};
Node::Ctor(..) => self.attrs(self.get_parent_item(id)),
Node::Crate(item) => &item.attrs[..],
Node::MacroDef(def) => def.attrs,
Node::AnonConst(..)
| Node::PathSegment(..)
| Node::Ty(..)
| Node::Pat(..)
| Node::Binding(..)
| Node::TraitRef(..)
| Node::Block(..)
| Node::Lifetime(..)
| Node::Visibility(..) => &[],
});
attrs.unwrap_or(&[])
}

28 changes: 8 additions & 20 deletions src/librustdoc/clean/inline.rs
Original file line number Diff line number Diff line change
@@ -124,16 +124,8 @@ crate fn try_inline(
let attrs = merge_attrs(cx, Some(parent_module), target_attrs, attrs_clone);

cx.renderinfo.borrow_mut().inlined.insert(did);
ret.push(clean::Item {
source: cx.tcx.def_span(did).clean(cx),
name: Some(name.clean(cx)),
attrs,
kind,
visibility: clean::Public,
stability: cx.tcx.lookup_stability(did).cloned(),
deprecation: cx.tcx.lookup_deprecation(did).clean(cx),
def_id: did,
});
let what_rustc_thinks = clean::Item::from_def_id_and_parts(did, Some(name), kind, cx);
ret.push(clean::Item { attrs, ..what_rustc_thinks });
Some(ret)
}

@@ -443,8 +435,10 @@ crate fn build_impl(

debug!("build_impl: impl {:?} for {:?}", trait_.def_id(), for_.def_id());

ret.push(clean::Item {
kind: clean::ImplItem(clean::Impl {
ret.push(clean::Item::from_def_id_and_parts(
did,
None,
clean::ImplItem(clean::Impl {
unsafety: hir::Unsafety::Normal,
generics,
provided_trait_methods: provided,
@@ -455,14 +449,8 @@ crate fn build_impl(
synthetic: false,
blanket_impl: None,
}),
source: tcx.def_span(did).clean(cx),
name: None,
attrs,
visibility: clean::Inherited,
stability: tcx.lookup_stability(did).cloned(),
deprecation: tcx.lookup_deprecation(did).clean(cx),
def_id: did,
});
cx,
));
}

fn build_module(cx: &DocContext<'_>, did: DefId, visited: &mut FxHashSet<DefId>) -> clean::Module {
385 changes: 144 additions & 241 deletions src/librustdoc/clean/mod.rs

Large diffs are not rendered by default.

53 changes: 50 additions & 3 deletions src/librustdoc/clean/types.rs
Original file line number Diff line number Diff line change
@@ -112,6 +112,48 @@ impl Item {
self.attrs.doc_value()
}

/// Convenience wrapper around [`Self::from_def_id_and_parts`] which converts
/// `hir_id` to a [`DefId`]
pub fn from_hir_id_and_parts(
hir_id: hir::HirId,
name: Option<Symbol>,
kind: ItemKind,
cx: &DocContext<'_>,
) -> Item {
Item::from_def_id_and_parts(cx.tcx.hir().local_def_id(hir_id).to_def_id(), name, kind, cx)
}

pub fn from_def_id_and_parts(
def_id: DefId,
name: Option<Symbol>,
kind: ItemKind,
cx: &DocContext<'_>,
) -> Item {
use super::Clean;

debug!("name={:?}, def_id={:?}", name, def_id);

// `span_if_local()` lies about functions and only gives the span of the function signature
let source = def_id.as_local().map_or_else(
|| cx.tcx.def_span(def_id),
|local| {
let hir = cx.tcx.hir();
hir.span_with_body(hir.local_def_id_to_hir_id(local))
},
);

Item {
def_id,
kind,
name: name.clean(cx),
source: source.clean(cx),
attrs: cx.tcx.get_attrs(def_id).clean(cx),
visibility: cx.tcx.visibility(def_id).clean(cx),
stability: cx.tcx.lookup_stability(def_id).cloned(),
deprecation: cx.tcx.lookup_deprecation(def_id).clean(cx),
}
}

/// Finds all `doc` attributes as NameValues and returns their corresponding values, joined
/// with newlines.
crate fn collapsed_doc_value(&self) -> Option<String> {
@@ -1460,12 +1502,17 @@ impl From<hir::PrimTy> for PrimitiveType {
}
}

#[derive(Clone, PartialEq, Eq, Debug)]
#[derive(Clone, Debug)]
crate enum Visibility {
Public,
Inherited,
Crate,
Restricted(DefId, Path),
Restricted(DefId, rustc_hir::definitions::DefPath),
}

impl Visibility {
crate fn is_public(&self) -> bool {
matches!(self, Visibility::Public)
}
}

#[derive(Clone, Debug)]
52 changes: 5 additions & 47 deletions src/librustdoc/doctree.rs
Original file line number Diff line number Diff line change
@@ -28,25 +28,19 @@ crate struct Module<'hir> {
crate statics: Vec<Static<'hir>>,
crate constants: Vec<Constant<'hir>>,
crate traits: Vec<Trait<'hir>>,
crate vis: &'hir hir::Visibility<'hir>,
crate impls: Vec<Impl<'hir>>,
crate foreigns: Vec<ForeignItem<'hir>>,
crate macros: Vec<Macro<'hir>>,
crate proc_macros: Vec<ProcMacro<'hir>>,
crate macros: Vec<Macro>,
crate proc_macros: Vec<ProcMacro>,
crate trait_aliases: Vec<TraitAlias<'hir>>,
crate is_crate: bool,
}

impl Module<'hir> {
crate fn new(
name: Option<Symbol>,
attrs: &'hir [ast::Attribute],
vis: &'hir hir::Visibility<'hir>,
) -> Module<'hir> {
crate fn new(name: Option<Symbol>, attrs: &'hir [ast::Attribute]) -> Module<'hir> {
Module {
name,
id: hir::CRATE_HIR_ID,
vis,
where_outer: rustc_span::DUMMY_SP,
where_inner: rustc_span::DUMMY_SP,
attrs,
@@ -83,53 +77,39 @@ crate enum StructType {
}

crate struct Struct<'hir> {
crate vis: &'hir hir::Visibility<'hir>,
crate id: hir::HirId,
crate struct_type: StructType,
crate name: Symbol,
crate generics: &'hir hir::Generics<'hir>,
crate attrs: &'hir [ast::Attribute],
crate fields: &'hir [hir::StructField<'hir>],
crate span: Span,
}

crate struct Union<'hir> {
crate vis: &'hir hir::Visibility<'hir>,
crate id: hir::HirId,
crate struct_type: StructType,
crate name: Symbol,
crate generics: &'hir hir::Generics<'hir>,
crate attrs: &'hir [ast::Attribute],
crate fields: &'hir [hir::StructField<'hir>],
crate span: Span,
}

crate struct Enum<'hir> {
crate vis: &'hir hir::Visibility<'hir>,
crate variants: Vec<Variant<'hir>>,
crate generics: &'hir hir::Generics<'hir>,
crate attrs: &'hir [ast::Attribute],
crate id: hir::HirId,
crate span: Span,
crate name: Symbol,
}

crate struct Variant<'hir> {
crate name: Symbol,
crate id: hir::HirId,
crate attrs: &'hir [ast::Attribute],
crate def: &'hir hir::VariantData<'hir>,
crate span: Span,
}

crate struct Function<'hir> {
crate decl: &'hir hir::FnDecl<'hir>,
crate attrs: &'hir [ast::Attribute],
crate id: hir::HirId,
crate name: Symbol,
crate vis: &'hir hir::Visibility<'hir>,
crate header: hir::FnHeader,
crate span: Span,
crate generics: &'hir hir::Generics<'hir>,
crate body: hir::BodyId,
}
@@ -139,18 +119,12 @@ crate struct Typedef<'hir> {
crate gen: &'hir hir::Generics<'hir>,
crate name: Symbol,
crate id: hir::HirId,
crate attrs: &'hir [ast::Attribute],
crate span: Span,
crate vis: &'hir hir::Visibility<'hir>,
}

crate struct OpaqueTy<'hir> {
crate opaque_ty: &'hir hir::OpaqueTy<'hir>,
crate name: Symbol,
crate id: hir::HirId,
crate attrs: &'hir [ast::Attribute],
crate span: Span,
crate vis: &'hir hir::Visibility<'hir>,
}

#[derive(Debug)]
@@ -169,10 +143,7 @@ crate struct Constant<'hir> {
crate type_: &'hir hir::Ty<'hir>,
crate expr: hir::BodyId,
crate name: Symbol,
crate attrs: &'hir [ast::Attribute],
crate vis: &'hir hir::Visibility<'hir>,
crate id: hir::HirId,
crate span: Span,
}

crate struct Trait<'hir> {
@@ -184,18 +155,13 @@ crate struct Trait<'hir> {
crate bounds: &'hir [hir::GenericBound<'hir>],
crate attrs: &'hir [ast::Attribute],
crate id: hir::HirId,
crate span: Span,
crate vis: &'hir hir::Visibility<'hir>,
}

crate struct TraitAlias<'hir> {
crate name: Symbol,
crate generics: &'hir hir::Generics<'hir>,
crate bounds: &'hir [hir::GenericBound<'hir>],
crate attrs: &'hir [ast::Attribute],
crate id: hir::HirId,
crate span: Span,
crate vis: &'hir hir::Visibility<'hir>,
}

#[derive(Debug)]
@@ -215,22 +181,16 @@ crate struct Impl<'hir> {
}

crate struct ForeignItem<'hir> {
crate vis: &'hir hir::Visibility<'hir>,
crate id: hir::HirId,
crate name: Symbol,
crate kind: &'hir hir::ForeignItemKind<'hir>,
crate attrs: &'hir [ast::Attribute],
crate span: Span,
}

// For Macro we store the DefId instead of the NodeId, since we also create
// these imported macro_rules (which only have a DUMMY_NODE_ID).
crate struct Macro<'hir> {
crate struct Macro {
crate name: Symbol,
crate hid: hir::HirId,
crate def_id: hir::def_id::DefId,
crate attrs: &'hir [ast::Attribute],
crate span: Span,
crate matchers: Vec<Span>,
crate imported_from: Option<Symbol>,
}
@@ -256,13 +216,11 @@ crate struct Import<'hir> {
crate span: Span,
}

crate struct ProcMacro<'hir> {
crate struct ProcMacro {
crate name: Symbol,
crate id: hir::HirId,
crate kind: MacroKind,
crate helpers: Vec<Symbol>,
crate attrs: &'hir [ast::Attribute],
crate span: Span,
}

crate fn struct_type_from_def(vdata: &hir::VariantData<'_>) -> StructType {
24 changes: 18 additions & 6 deletions src/librustdoc/html/format.rs
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ use std::fmt;

use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_span::def_id::DefId;
use rustc_span::def_id::{DefId, CRATE_DEF_INDEX};
use rustc_target::spec::abi::Abi;

use crate::clean::{self, PrimitiveType};
@@ -1089,19 +1089,31 @@ impl Function<'_> {

impl clean::Visibility {
crate fn print_with_space(&self) -> impl fmt::Display + '_ {
use rustc_span::symbol::kw;

display_fn(move |f| match *self {
clean::Public => f.write_str("pub "),
clean::Inherited => Ok(()),
clean::Visibility::Crate => write!(f, "pub(crate) "),
// If this is `pub(crate)`, `path` will be empty.
clean::Visibility::Restricted(did, _) if did.index == CRATE_DEF_INDEX => {
write!(f, "pub(crate) ")
}
clean::Visibility::Restricted(did, ref path) => {
f.write_str("pub(")?;
if path.segments.len() != 1
|| (path.segments[0].name != "self" && path.segments[0].name != "super")
debug!("path={:?}", path);
let first_name =
path.data[0].data.get_opt_name().expect("modules are always named");
if path.data.len() != 1 || (first_name != kw::SelfLower && first_name != kw::Super)
{
f.write_str("in ")?;
}
resolved_path(f, did, path, true, false)?;
f.write_str(") ")
// modified from `resolved_path()` to work with `DefPathData`
let last_name = path.data.last().unwrap().data.get_opt_name().unwrap();
for seg in &path.data[..path.data.len() - 1] {
write!(f, "{}::", seg.data.get_opt_name().unwrap())?;
}
let path = anchor(did, &last_name.as_str()).to_string();
write!(f, "{}) ", path)
}
})
}
8 changes: 3 additions & 5 deletions src/librustdoc/passes/stripper.rs
Original file line number Diff line number Diff line change
@@ -50,13 +50,13 @@ impl<'a> DocFolder for Stripper<'a> {
}

clean::StructFieldItem(..) => {
if i.visibility != clean::Public {
if !i.visibility.is_public() {
return StripItem(i).strip();
}
}

clean::ModuleItem(..) => {
if i.def_id.is_local() && i.visibility != clean::Public {
if i.def_id.is_local() && !i.visibility.is_public() {
debug!("Stripper: stripping module {:?}", i.name);
let old = mem::replace(&mut self.update_retained, false);
let ret = StripItem(self.fold_item_recur(i).unwrap()).strip();
@@ -163,9 +163,7 @@ crate struct ImportStripper;
impl DocFolder for ImportStripper {
fn fold_item(&mut self, i: Item) -> Option<Item> {
match i.kind {
clean::ExternCrateItem(..) | clean::ImportItem(..) if i.visibility != clean::Public => {
None
}
clean::ExternCrateItem(..) | clean::ImportItem(..) if !i.visibility.is_public() => None,
_ => self.fold_item_recur(i),
}
}
109 changes: 11 additions & 98 deletions src/librustdoc/visit_ast.rs
Original file line number Diff line number Diff line change
@@ -91,16 +91,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
) -> Struct<'tcx> {
debug!("visiting struct");
let struct_type = struct_type_from_def(&*sd);
Struct {
id: item.hir_id,
struct_type,
name,
vis: &item.vis,
attrs: &item.attrs,
generics,
fields: sd.fields(),
span: item.span,
}
Struct { id: item.hir_id, struct_type, name, generics, fields: sd.fields() }
}

fn visit_union_data(
@@ -112,16 +103,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
) -> Union<'tcx> {
debug!("visiting union");
let struct_type = struct_type_from_def(&*sd);
Union {
id: item.hir_id,
struct_type,
name,
vis: &item.vis,
attrs: &item.attrs,
generics,
fields: sd.fields(),
span: item.span,
}
Union { id: item.hir_id, struct_type, name, generics, fields: sd.fields() }
}

fn visit_enum_def(
@@ -137,19 +119,10 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
variants: def
.variants
.iter()
.map(|v| Variant {
name: v.ident.name,
id: v.id,
attrs: &v.attrs,
def: &v.data,
span: v.span,
})
.map(|v| Variant { name: v.ident.name, id: v.id, def: &v.data })
.collect(),
vis: &it.vis,
generics,
attrs: &it.attrs,
id: it.hir_id,
span: it.span,
}
}

@@ -202,27 +175,10 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
}
}

om.proc_macros.push(ProcMacro {
name,
id: item.hir_id,
kind,
helpers,
attrs: &item.attrs,
span: item.span,
});
om.proc_macros.push(ProcMacro { name, id: item.hir_id, kind, helpers });
}
None => {
om.fns.push(Function {
id: item.hir_id,
vis: &item.vis,
attrs: &item.attrs,
decl,
name,
span: item.span,
generics,
header,
body,
});
om.fns.push(Function { id: item.hir_id, decl, name, generics, header, body });
}
}
}
@@ -236,7 +192,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
m: &'tcx hir::Mod<'tcx>,
name: Option<Symbol>,
) -> Module<'tcx> {
let mut om = Module::new(name, attrs, vis);
let mut om = Module::new(name, attrs);
om.where_outer = span;
om.where_inner = m.inner;
om.id = id;
@@ -471,26 +427,11 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
self.visit_fn(om, item, ident.name, &sig.decl, sig.header, gen, body)
}
hir::ItemKind::TyAlias(ty, ref gen) => {
let t = Typedef {
ty,
gen,
name: ident.name,
id: item.hir_id,
attrs: &item.attrs,
span: item.span,
vis: &item.vis,
};
let t = Typedef { ty, gen, name: ident.name, id: item.hir_id };
om.typedefs.push(t);
}
hir::ItemKind::OpaqueTy(ref opaque_ty) => {
let t = OpaqueTy {
opaque_ty,
name: ident.name,
id: item.hir_id,
attrs: &item.attrs,
span: item.span,
vis: &item.vis,
};
let t = OpaqueTy { opaque_ty, name: ident.name, id: item.hir_id };
om.opaque_tys.push(t);
}
hir::ItemKind::Static(type_, mutability, expr) => {
@@ -510,15 +451,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
// Underscore constants do not correspond to a nameable item and
// so are never useful in documentation.
if ident.name != kw::Underscore {
let s = Constant {
type_,
expr,
id: item.hir_id,
name: ident.name,
attrs: &item.attrs,
span: item.span,
vis: &item.vis,
};
let s = Constant { type_, expr, id: item.hir_id, name: ident.name };
om.constants.push(s);
}
}
@@ -533,21 +466,11 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
bounds,
id: item.hir_id,
attrs: &item.attrs,
span: item.span,
vis: &item.vis,
};
om.traits.push(t);
}
hir::ItemKind::TraitAlias(ref generics, ref bounds) => {
let t = TraitAlias {
name: ident.name,
generics,
bounds,
id: item.hir_id,
attrs: &item.attrs,
span: item.span,
vis: &item.vis,
};
let t = TraitAlias { name: ident.name, generics, bounds, id: item.hir_id };
om.trait_aliases.push(t);
}

@@ -602,29 +525,19 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
id: item.hir_id,
name: renamed.unwrap_or(item.ident).name,
kind: &item.kind,
vis: &item.vis,
attrs: &item.attrs,
span: item.span,
});
}

// Convert each `exported_macro` into a doc item.
fn visit_local_macro(
&self,
def: &'tcx hir::MacroDef<'_>,
renamed: Option<Symbol>,
) -> Macro<'tcx> {
fn visit_local_macro(&self, def: &'tcx hir::MacroDef<'_>, renamed: Option<Symbol>) -> Macro {
debug!("visit_local_macro: {}", def.ident);
let tts = def.ast.body.inner_tokens().trees().collect::<Vec<_>>();
// Extract the spans of all matchers. They represent the "interface" of the macro.
let matchers = tts.chunks(4).map(|arm| arm[0].span()).collect();

Macro {
hid: def.hir_id,
def_id: self.cx.tcx.hir().local_def_id(def.hir_id).to_def_id(),
attrs: &def.attrs,
name: renamed.unwrap_or(def.ident.name),
span: def.span,
matchers,
imported_from: None,
}
2 changes: 2 additions & 0 deletions src/test/rustdoc/issue-32395.rs
Original file line number Diff line number Diff line change
@@ -4,10 +4,12 @@

// @has variant_struct/enum.Foo.html
// @!has - 'pub qux'
// @!has - 'pub(crate) qux'
// @!has - 'pub Bar'
extern crate variant_struct;

// @has issue_32395/enum.Foo.html
// @!has - 'pub qux'
// @!has - 'pub(crate) qux'
// @!has - 'pub Bar'
pub use variant_struct::Foo;
16 changes: 7 additions & 9 deletions src/test/rustdoc/pub-restricted.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// ignore-tidy-linelength

// compile-flags: --document-private-items

#![feature(crate_visibility_modifier)]
@@ -12,21 +10,21 @@ pub struct FooPublic;
crate struct FooJustCrate;
// @has 'foo/struct.FooPubCrate.html' '//pre' 'pub(crate) struct FooPubCrate'
pub(crate) struct FooPubCrate;
// @has 'foo/struct.FooSelf.html' '//pre' 'pub(self) struct FooSelf'
// @has 'foo/struct.FooSelf.html' '//pre' 'pub(crate) struct FooSelf'
pub(self) struct FooSelf;
// @has 'foo/struct.FooInSelf.html' '//pre' 'pub(self) struct FooInSelf'
// @has 'foo/struct.FooInSelf.html' '//pre' 'pub(crate) struct FooInSelf'
pub(in self) struct FooInSelf;
mod a {
// @has 'foo/a/struct.FooSuper.html' '//pre' 'pub(super) struct FooSuper'
// @has 'foo/a/struct.FooSuper.html' '//pre' 'pub(crate) struct FooSuper'
pub(super) struct FooSuper;
// @has 'foo/a/struct.FooInSuper.html' '//pre' 'pub(super) struct FooInSuper'
// @has 'foo/a/struct.FooInSuper.html' '//pre' 'pub(crate) struct FooInSuper'
pub(in super) struct FooInSuper;
// @has 'foo/a/struct.FooInA.html' '//pre' 'pub(in a) struct FooInA'
pub(in a) struct FooInA;
mod b {
// @has 'foo/a/b/struct.FooInSelfSuperB.html' '//pre' 'pub(in self::super::b) struct FooInSelfSuperB'
pub(in self::super::b) struct FooInSelfSuperB;
// @has 'foo/a/b/struct.FooInSuperSuper.html' '//pre' 'pub(in super::super) struct FooInSuperSuper'
// @has 'foo/a/b/struct.FooInSelfSuperB.html' '//pre' 'pub(in a::b) struct FooInSelfSuperB'
pub(in a::b) struct FooInSelfSuperB;
// @has 'foo/a/b/struct.FooInSuperSuper.html' '//pre' 'pub(crate) struct FooInSuperSuper'
pub(in super::super) struct FooInSuperSuper;
// @has 'foo/a/b/struct.FooInAB.html' '//pre' 'pub(in a::b) struct FooInAB'
pub(in a::b) struct FooInAB;