Skip to content

Commit 6e8c327

Browse files
committed
Collect and use #[doc(test(attr(..)))] at every level
1 parent 2e946d4 commit 6e8c327

File tree

3 files changed

+285
-34
lines changed

3 files changed

+285
-34
lines changed

src/librustdoc/doctest/rust.rs

+23-34
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,26 @@ impl HirCollector<'_> {
106106
return;
107107
}
108108

109+
// Try collecting `#[doc(test(attr(...)))]`
110+
let old_global_crate_attrs_len = self.collector.global_crate_attrs.len();
111+
for doc_test_attrs in ast_attrs
112+
.iter()
113+
.filter(|a| a.has_name(sym::doc))
114+
.flat_map(|a| a.meta_item_list().unwrap_or_default())
115+
.filter(|a| a.has_name(sym::test))
116+
{
117+
let Some(doc_test_attrs) = doc_test_attrs.meta_item_list() else { continue };
118+
for attr in doc_test_attrs
119+
.iter()
120+
.filter(|a| a.has_name(sym::attr))
121+
.flat_map(|a| a.meta_item_list().unwrap_or_default())
122+
.map(|i| pprust::meta_list_item_to_string(i))
123+
{
124+
// Add the additional attributes to the global_crate_attrs vector
125+
self.collector.global_crate_attrs.push(attr);
126+
}
127+
}
128+
109129
let mut has_name = false;
110130
if let Some(name) = name {
111131
self.collector.cur_path.push(name);
@@ -140,6 +160,9 @@ impl HirCollector<'_> {
140160

141161
nested(self);
142162

163+
// Restore global_crate_attrs to it's previous size/content
164+
self.collector.global_crate_attrs.truncate(old_global_crate_attrs_len);
165+
143166
if has_name {
144167
self.collector.cur_path.pop();
145168
}
@@ -153,40 +176,6 @@ impl<'tcx> intravisit::Visitor<'tcx> for HirCollector<'tcx> {
153176
self.tcx
154177
}
155178

156-
fn visit_mod(&mut self, m: &'tcx hir::Mod<'tcx>, _s: Span, hir_id: hir::HirId) {
157-
let attrs = self.tcx.hir_attrs(hir_id);
158-
159-
if !attrs.is_empty() {
160-
// Try collecting `#![doc(test(attr(...)))]` from the attribute module
161-
let old_len = self.collector.global_crate_attrs.len();
162-
for doc_test_attrs in attrs
163-
.iter()
164-
.filter(|a| a.has_name(sym::doc))
165-
.flat_map(|a| a.meta_item_list().unwrap_or_default())
166-
.filter(|a| a.has_name(sym::test))
167-
{
168-
let Some(doc_test_attrs) = doc_test_attrs.meta_item_list() else { continue };
169-
for attr in doc_test_attrs
170-
.iter()
171-
.filter(|a| a.has_name(sym::attr))
172-
.flat_map(|a| a.meta_item_list().unwrap_or_default())
173-
.map(|i| pprust::meta_list_item_to_string(i))
174-
{
175-
// Add the additional attributes to the global_crate_attrs vector
176-
self.collector.global_crate_attrs.push(attr);
177-
}
178-
}
179-
180-
let r = intravisit::walk_mod(self, m);
181-
182-
// Restore global_crate_attrs to it's previous size/content
183-
self.collector.global_crate_attrs.truncate(old_len);
184-
r
185-
} else {
186-
intravisit::walk_mod(self, m)
187-
}
188-
}
189-
190179
fn visit_item(&mut self, item: &'tcx hir::Item<'_>) {
191180
let name = match &item.kind {
192181
hir::ItemKind::Impl(impl_) => {
+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
// Same test as dead-code-module but with 2 doc(test(attr())) at different levels.
2+
3+
//@ edition: 2024
4+
//@ compile-flags:--test --test-args=--test-threads=1
5+
//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR"
6+
//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
7+
//@ failure-status: 101
8+
9+
#![doc(test(attr(deny(warnings))))]
10+
11+
#[doc(test(attr(allow(dead_code))))]
12+
/// Example
13+
///
14+
/// ```rust,no_run
15+
/// trait OnlyWarning { fn no_deny_warnings(); }
16+
/// ```
17+
static S: u32 = 5;
18+
19+
#[doc(test(attr(allow(dead_code))))]
20+
/// Example
21+
///
22+
/// ```rust,no_run
23+
/// let unused_error = 5;
24+
///
25+
/// fn dead_code_but_no_error() {}
26+
/// ```
27+
const C: u32 = 5;
28+
29+
#[doc(test(attr(allow(dead_code))))]
30+
/// Example
31+
///
32+
/// ```rust,no_run
33+
/// trait OnlyWarningAtA { fn no_deny_warnings(); }
34+
/// ```
35+
struct A {
36+
#[doc(test(attr(deny(dead_code))))]
37+
/// Example
38+
///
39+
/// ```rust,no_run
40+
/// trait DeadCodeInField {}
41+
/// ```
42+
field: u32
43+
}
44+
45+
#[doc(test(attr(allow(dead_code))))]
46+
/// Example
47+
///
48+
/// ```rust,no_run
49+
/// trait OnlyWarningAtU { fn no_deny_warnings(); }
50+
/// ```
51+
union U {
52+
#[doc(test(attr(deny(dead_code))))]
53+
/// Example
54+
///
55+
/// ```rust,no_run
56+
/// trait DeadCodeInUnionField {}
57+
/// ```
58+
field: u32,
59+
/// Example
60+
///
61+
/// ```rust,no_run
62+
/// trait NotDeadCodeInUnionField {}
63+
/// ```
64+
field2: u64,
65+
}
66+
67+
#[doc(test(attr(deny(dead_code))))]
68+
/// Example
69+
///
70+
/// ```rust,no_run
71+
/// let not_dead_code_but_unused = 5;
72+
/// ```
73+
enum Enum {
74+
#[doc(test(attr(allow(dead_code))))]
75+
/// Example
76+
///
77+
/// ```rust,no_run
78+
/// trait NotDeadCodeInVariant {}
79+
///
80+
/// fn main() { let unused_in_variant = 5; }
81+
/// ```
82+
Variant1,
83+
}
84+
85+
#[doc(test(attr(allow(dead_code))))]
86+
/// Example
87+
///
88+
/// ```rust,no_run
89+
/// trait OnlyWarningAtImplA { fn no_deny_warnings(); }
90+
/// ```
91+
impl A {
92+
/// Example
93+
///
94+
/// ```rust,no_run
95+
/// trait NotDeadCodeInImplMethod {}
96+
/// ```
97+
fn method() {}
98+
}
99+
100+
#[doc(test(attr(deny(dead_code))))]
101+
/// Example
102+
///
103+
/// ```rust,no_run
104+
/// trait StillDeadCodeAtMyTrait { }
105+
/// ```
106+
trait MyTrait {
107+
#[doc(test(attr(allow(dead_code))))]
108+
/// Example
109+
///
110+
/// ```rust,no_run
111+
/// trait NotDeadCodeAtImplFn {}
112+
///
113+
/// fn main() { let unused_in_impl = 5; }
114+
/// ```
115+
fn my_trait_fn();
116+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
2+
running 13 tests
3+
test $DIR/dead-code-items.rs - A (line 32) - compile ... ok
4+
test $DIR/dead-code-items.rs - A (line 88) - compile ... ok
5+
test $DIR/dead-code-items.rs - A::field (line 39) - compile ... FAILED
6+
test $DIR/dead-code-items.rs - A::method (line 94) - compile ... ok
7+
test $DIR/dead-code-items.rs - C (line 22) - compile ... FAILED
8+
test $DIR/dead-code-items.rs - Enum (line 70) - compile ... FAILED
9+
test $DIR/dead-code-items.rs - Enum::Variant1 (line 77) - compile ... FAILED
10+
test $DIR/dead-code-items.rs - MyTrait (line 103) - compile ... FAILED
11+
test $DIR/dead-code-items.rs - MyTrait::my_trait_fn (line 110) - compile ... FAILED
12+
test $DIR/dead-code-items.rs - S (line 14) - compile ... ok
13+
test $DIR/dead-code-items.rs - U (line 48) - compile ... ok
14+
test $DIR/dead-code-items.rs - U::field (line 55) - compile ... FAILED
15+
test $DIR/dead-code-items.rs - U::field2 (line 61) - compile ... ok
16+
17+
failures:
18+
19+
---- $DIR/dead-code-items.rs - A::field (line 39) stdout ----
20+
error: trait `DeadCodeInField` is never used
21+
--> $DIR/dead-code-items.rs:40:7
22+
|
23+
LL | trait DeadCodeInField {}
24+
| ^^^^^^^^^^^^^^^
25+
|
26+
note: the lint level is defined here
27+
--> $DIR/dead-code-items.rs:38:9
28+
|
29+
LL | #![deny(dead_code)]
30+
| ^^^^^^^^^
31+
32+
error: aborting due to 1 previous error
33+
34+
Couldn't compile the test.
35+
---- $DIR/dead-code-items.rs - C (line 22) stdout ----
36+
error: unused variable: `unused_error`
37+
--> $DIR/dead-code-items.rs:23:5
38+
|
39+
LL | let unused_error = 5;
40+
| ^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused_error`
41+
|
42+
note: the lint level is defined here
43+
--> $DIR/dead-code-items.rs:20:9
44+
|
45+
LL | #![deny(warnings)]
46+
| ^^^^^^^^
47+
= note: `#[deny(unused_variables)]` implied by `#[deny(warnings)]`
48+
49+
error: aborting due to 1 previous error
50+
51+
Couldn't compile the test.
52+
---- $DIR/dead-code-items.rs - Enum (line 70) stdout ----
53+
error: unused variable: `not_dead_code_but_unused`
54+
--> $DIR/dead-code-items.rs:71:5
55+
|
56+
LL | let not_dead_code_but_unused = 5;
57+
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_not_dead_code_but_unused`
58+
|
59+
note: the lint level is defined here
60+
--> $DIR/dead-code-items.rs:68:9
61+
|
62+
LL | #![deny(warnings)]
63+
| ^^^^^^^^
64+
= note: `#[deny(unused_variables)]` implied by `#[deny(warnings)]`
65+
66+
error: aborting due to 1 previous error
67+
68+
Couldn't compile the test.
69+
---- $DIR/dead-code-items.rs - Enum::Variant1 (line 77) stdout ----
70+
error: unused variable: `unused_in_variant`
71+
--> $DIR/dead-code-items.rs:80:17
72+
|
73+
LL | fn main() { let unused_in_variant = 5; }
74+
| ^^^^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused_in_variant`
75+
|
76+
note: the lint level is defined here
77+
--> $DIR/dead-code-items.rs:75:9
78+
|
79+
LL | #![deny(warnings)]
80+
| ^^^^^^^^
81+
= note: `#[deny(unused_variables)]` implied by `#[deny(warnings)]`
82+
83+
error: aborting due to 1 previous error
84+
85+
Couldn't compile the test.
86+
---- $DIR/dead-code-items.rs - MyTrait (line 103) stdout ----
87+
error: trait `StillDeadCodeAtMyTrait` is never used
88+
--> $DIR/dead-code-items.rs:104:7
89+
|
90+
LL | trait StillDeadCodeAtMyTrait { }
91+
| ^^^^^^^^^^^^^^^^^^^^^^
92+
|
93+
note: the lint level is defined here
94+
--> $DIR/dead-code-items.rs:102:9
95+
|
96+
LL | #![deny(dead_code)]
97+
| ^^^^^^^^^
98+
99+
error: aborting due to 1 previous error
100+
101+
Couldn't compile the test.
102+
---- $DIR/dead-code-items.rs - MyTrait::my_trait_fn (line 110) stdout ----
103+
error: unused variable: `unused_in_impl`
104+
--> $DIR/dead-code-items.rs:113:17
105+
|
106+
LL | fn main() { let unused_in_impl = 5; }
107+
| ^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused_in_impl`
108+
|
109+
note: the lint level is defined here
110+
--> $DIR/dead-code-items.rs:108:9
111+
|
112+
LL | #![deny(warnings)]
113+
| ^^^^^^^^
114+
= note: `#[deny(unused_variables)]` implied by `#[deny(warnings)]`
115+
116+
error: aborting due to 1 previous error
117+
118+
Couldn't compile the test.
119+
---- $DIR/dead-code-items.rs - U::field (line 55) stdout ----
120+
error: trait `DeadCodeInUnionField` is never used
121+
--> $DIR/dead-code-items.rs:56:7
122+
|
123+
LL | trait DeadCodeInUnionField {}
124+
| ^^^^^^^^^^^^^^^^^^^^
125+
|
126+
note: the lint level is defined here
127+
--> $DIR/dead-code-items.rs:54:9
128+
|
129+
LL | #![deny(dead_code)]
130+
| ^^^^^^^^^
131+
132+
error: aborting due to 1 previous error
133+
134+
Couldn't compile the test.
135+
136+
failures:
137+
$DIR/dead-code-items.rs - A::field (line 39)
138+
$DIR/dead-code-items.rs - C (line 22)
139+
$DIR/dead-code-items.rs - Enum (line 70)
140+
$DIR/dead-code-items.rs - Enum::Variant1 (line 77)
141+
$DIR/dead-code-items.rs - MyTrait (line 103)
142+
$DIR/dead-code-items.rs - MyTrait::my_trait_fn (line 110)
143+
$DIR/dead-code-items.rs - U::field (line 55)
144+
145+
test result: FAILED. 6 passed; 7 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
146+

0 commit comments

Comments
 (0)