Repository: rust
Issue: Rustdoc lints don't fire for pub re-exports
Labels: T-rustdoc,A-lints,C-bug,A-cross-crate-reexports

This is the general form of #77200, since it appears this was unintentional. It came up again in https://github.com/rust-lang/rust/pull/77119#issuecomment-699484891.

I tried this code:

```rust
#![deny(missing_doc_code_examples)]
//! ```
//! unimplemented!()
//! ```

pub use std::task::RawWakerVTable;
```

I expected to see this happen: Rustdoc gives an error that `RawWakerVTable` is missing examples.

Instead, this happened: Rustdoc ignores the re-export.

### Meta

`rustdoc --version`: rustdoc 1.48.0-nightly (8b4085359 2020-09-23)

### Possible Cause

@GuillaumeGomez and I found yesterday that rustdoc does _not_ generate an [`ImportItem`](https://doc.rust-lang.org/nightly/nightly-rustc/rustdoc/clean/types/enum.ItemEnum.html#variant.ImportItem) for pub re-exports. Instead it generates an [`StructItem`](https://doc.rust-lang.org/nightly/nightly-rustc/rustdoc/clean/types/enum.ItemEnum.html#variant.StructItem), which has the [`DefId`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/def_id/struct.DefId.html) of the original item, not the [`HirId`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir_id/struct.HirId.html) of an item in the current crate. This means that anywhere that needs a HirId, like lints, is out of luck.

Relevant pieces of code:

https://github.com/rust-lang/rust/blob/dadde88ebacd5e209922928206e0d85a99009203/src/librustdoc/passes/calculate_doc_coverage.rs#L184-L188
https://github.com/rust-lang/rust/blob/2e7ca7329966b9a86b4d35689cb719883dcda4e8/src/librustdoc/passes/doc_test_lints.rs#L76-L82
https://github.com/rust-lang/rust/blob/f9546afbe1300fa3780470db1d10eb60c2cd3bbc/src/librustdoc/passes/html_tags.rs#L106-L112

`check_code_block_syntax` doesn't have this issue, but only by sidestepping the lint machinery altogether and being impossible to silence.
https://github.com/rust-lang/rust/blob/2e7ca7329966b9a86b4d35689cb719883dcda4e8/src/librustdoc/passes/check_code_block_syntax.rs#L60

I'm not yet sure what's causing the issues in `clean` - it _looks_ like it should be making an `ImportItem` but it's not happening in practice.

https://github.com/rust-lang/rust/blob/0bcc96dd3d54d459f3ae9a178bac3e7e944a9102/src/librustdoc/clean/mod.rs#L2284-L2293

Below is some debug output from the original code sample. There are various other `RawWakerVTable` items being skipped but they're either fields or impls, so I didn't include them here. In particular there are no `ImportItem`s.

<details><summary>Debug output</summary>

```
Sep 26 10:09:35.580 DEBUG rustdoc::passes::doc_test_lints: skipping foreign item DefId(2:33796 ~ core[e6b8]::task[0]::wake[0]::RawWakerVTable[0]) with inner item StructItem(Struct { struct_type: Plain, generics: Generics { params: [], where_predicates: [] }, fields: [Item { source: Span { filename: Real(Named("/home/joshua/rustc/library/core/src/task/wake.rs")), cnum: crate2, loline: 67, locol: 4, hiline: 67, hicol: 43, original: /home/joshua/rustc/library/core/src/task/wake.rs:67:5: 67:44 (#0) }, name: Some("clone"), attrs: Attributes { doc_strings: [SugaredDoc(0, /home/joshua/rustc/library/core/src/task/wake.rs:60:5: 66:83 (#0), "This function will be called when the [`RawWaker`] gets cloned, e.g. when\nthe [`Waker`] in which the [`RawWaker`] is stored gets cloned.\n\nThe implementation of this function must retain all resources that are\nrequired for this additional instance of a [`RawWaker`] and associated\ntask. Calling `wake` on the resulting [`RawWaker`] should result in a wakeup\nof the same task that would have been awoken by the original [`RawWaker`].")], other_attrs: [], cfg: None, span: Some(/home/joshua/rustc/library/core/src/task/wake.rs:60:5: 60:82 (#0)), links: [], inner_docs: false }, inner: StructFieldItem(BareFunction(BareFunctionDecl { unsafety: Unsafe, generic_params: [], decl: FnDecl { inputs: Arguments { values: [Argument { type_: RawPointer(Not, Tuple([])), name: "" }] }, output: Return(ResolvedPath { path: Path { global: false, res: Err, segments: [PathSegment { name: "RawWaker", args: AngleBracketed { args: [], bindings: [] } }] }, param_names: None, did: DefId(2:33787 ~ core[e6b8]::task[0]::wake[0]::RawWaker[0]), is_generic: false }), c_variadic: false, attrs: Attributes { doc_strings: [], other_attrs: [], cfg: None, span: None, links: [], inner_docs: false } }, abi: Rust })), visibility: Inherited, def_id: DefId(2:33797 ~ core[e6b8]::task[0]::wake[0]::RawWakerVTable[0]::clone[0]), stability: None, deprecation: None }, Item { source: Span { filename: Real(Named("/home/joshua/rustc/library/core/src/task/wake.rs")), cnum: crate2, loline: 75, locol: 4, hiline: 75, hicol: 30, original: /home/joshua/rustc/library/core/src/task/wake.rs:75:5: 75:31 (#0) }, name: Some("wake"), attrs: Attributes { doc_strings: [SugaredDoc(0, /home/joshua/rustc/library/core/src/task/wake.rs:69:5: 74:25 (#0), "This function will be called when `wake` is called on the [`Waker`].\nIt must wake up the task associated with this [`RawWaker`].\n\nThe implementation of this function must make sure to release any\nresources that are associated with this instance of a [`RawWaker`] and\nassociated task.")], other_attrs: [], cfg: None, span: Some(/home/joshua/rustc/library/core/src/task/wake.rs:69:5: 69:77 (#0)), links: [], inner_docs: false }, inner: StructFieldItem(BareFunction(BareFunctionDecl { unsafety: Unsafe, generic_params: [], decl: FnDecl { inputs: Arguments { values: [Argument { type_: RawPointer(Not, Tuple([])), name: "" }] }, output: Return(Tuple([])), c_variadic: false, attrs: Attributes { doc_strings: [], other_attrs: [], cfg: None, span: None, links: [], inner_docs: false } }, abi: Rust })), visibility: Inherited, def_id: DefId(2:33798 ~ core[e6b8]::task[0]::wake[0]::RawWakerVTable[0]::wake[0]), stability: None, deprecation: None }, Item { source: Span { filename: Real(Named("/home/joshua/rustc/library/core/src/task/wake.rs")), cnum: crate2, loline: 82, locol: 4, hiline: 82, hicol: 37, original: /home/joshua/rustc/library/core/src/task/wake.rs:82:5: 82:38 (#0) }, name: Some("wake_by_ref"), attrs: Attributes { doc_strings: [SugaredDoc(0, /home/joshua/rustc/library/core/src/task/wake.rs:77:5: 81:17 (#0), "This function will be called when `wake_by_ref` is called on the [`Waker`].\nIt must wake up the task associated with this [`RawWaker`].\n\nThis function is similar to `wake`, but must not consume the provided data\npointer.")], other_attrs: [], cfg: None, span: Some(/home/joshua/rustc/library/core/src/task/wake.rs:77:5: 77:84 (#0)), links: [], inner_docs: false }, inner: StructFieldItem(BareFunction(BareFunctionDecl { unsafety: Unsafe, generic_params: [], decl: FnDecl { inputs: Arguments { values: [Argument { type_: RawPointer(Not, Tuple([])), name: "" }] }, output: Return(Tuple([])), c_variadic: false, attrs: Attributes { doc_strings: [], other_attrs: [], cfg: None, span: None, links: [], inner_docs: false } }, abi: Rust })), visibility: Inherited, def_id: DefId(2:33799 ~ core[e6b8]::task[0]::wake[0]::RawWakerVTable[0]::wake_by_ref[0]), stability: None, deprecation: None }, Item { source: Span { filename: Real(Named("/home/joshua/rustc/library/core/src/task/wake.rs")), cnum: crate2, loline: 89, locol: 4, hiline: 89, hicol: 30, original: /home/joshua/rustc/library/core/src/task/wake.rs:89:5: 89:31 (#0) }, name: Some("drop"), attrs: Attributes { doc_strings: [SugaredDoc(0, /home/joshua/rustc/library/core/src/task/wake.rs:84:5: 88:25 (#0), "This function gets called when a [`RawWaker`] gets dropped.\n\nThe implementation of this function must make sure to release any\nresources that are associated with this instance of a [`RawWaker`] and\nassociated task.")], other_attrs: [], cfg: None, span: Some(/home/joshua/rustc/library/core/src/task/wake.rs:84:5: 84:68 (#0)), links: [], inner_docs: false }, inner: StructFieldItem(BareFunction(BareFunctionDecl { unsafety: Unsafe, generic_params: [], decl: FnDecl { inputs: Arguments { values: [Argument { type_: RawPointer(Not, Tuple([])), name: "" }] }, output: Return(Tuple([])), c_variadic: false, attrs: Attributes { doc_strings: [], other_attrs: [], cfg: None, span: None, links: [], inner_docs: false } }, abi: Rust })), visibility: Inherited, def_id: DefId(2:33800 ~ core[e6b8]::task[0]::wake[0]::RawWakerVTable[0]::drop[0]), stability: None, deprecation: None }], fields_stripped: false })
```

</details>