Skip to content

Commit d96d3be

Browse files
committed
Collect and use #[doc(test(attr(..)))] at every level
1 parent 316e62a commit d96d3be

File tree

3 files changed

+285
-34
lines changed

3 files changed

+285
-34
lines changed

src/librustdoc/doctest/rust.rs

Lines changed: 23 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,26 @@ impl HirCollector<'_> {
127127
return;
128128
}
129129

130+
// Try collecting `#[doc(test(attr(...)))]`
131+
let old_global_crate_attrs_len = self.collector.global_crate_attrs.len();
132+
for doc_test_attrs in ast_attrs
133+
.iter()
134+
.filter(|a| a.has_name(sym::doc))
135+
.flat_map(|a| a.meta_item_list().unwrap_or_default())
136+
.filter(|a| a.has_name(sym::test))
137+
{
138+
let Some(doc_test_attrs) = doc_test_attrs.meta_item_list() else { continue };
139+
for attr in doc_test_attrs
140+
.iter()
141+
.filter(|a| a.has_name(sym::attr))
142+
.flat_map(|a| a.meta_item_list().unwrap_or_default())
143+
.map(|i| pprust::meta_list_item_to_string(i))
144+
{
145+
// Add the additional attributes to the global_crate_attrs vector
146+
self.collector.global_crate_attrs.push(attr);
147+
}
148+
}
149+
130150
let mut has_name = false;
131151
if let Some(name) = name {
132152
self.collector.cur_path.push(name);
@@ -161,6 +181,9 @@ impl HirCollector<'_> {
161181

162182
nested(self);
163183

184+
// Restore global_crate_attrs to it's previous size/content
185+
self.collector.global_crate_attrs.truncate(old_global_crate_attrs_len);
186+
164187
if has_name {
165188
self.collector.cur_path.pop();
166189
}
@@ -174,40 +197,6 @@ impl<'tcx> intravisit::Visitor<'tcx> for HirCollector<'tcx> {
174197
self.tcx
175198
}
176199

177-
fn visit_mod(&mut self, m: &'tcx hir::Mod<'tcx>, _s: Span, hir_id: hir::HirId) {
178-
let attrs = self.tcx.hir_attrs(hir_id);
179-
180-
if !attrs.is_empty() {
181-
// Try collecting `#![doc(test(attr(...)))]` from the attribute module
182-
let old_len = self.collector.global_crate_attrs.len();
183-
for doc_test_attrs in attrs
184-
.iter()
185-
.filter(|a| a.has_name(sym::doc))
186-
.flat_map(|a| a.meta_item_list().unwrap_or_default())
187-
.filter(|a| a.has_name(sym::test))
188-
{
189-
let Some(doc_test_attrs) = doc_test_attrs.meta_item_list() else { continue };
190-
for attr in doc_test_attrs
191-
.iter()
192-
.filter(|a| a.has_name(sym::attr))
193-
.flat_map(|a| a.meta_item_list().unwrap_or_default())
194-
.map(|i| pprust::meta_list_item_to_string(i))
195-
{
196-
// Add the additional attributes to the global_crate_attrs vector
197-
self.collector.global_crate_attrs.push(attr);
198-
}
199-
}
200-
201-
let r = intravisit::walk_mod(self, m);
202-
203-
// Restore global_crate_attrs to it's previous size/content
204-
self.collector.global_crate_attrs.truncate(old_len);
205-
r
206-
} else {
207-
intravisit::walk_mod(self, m)
208-
}
209-
}
210-
211200
fn visit_item(&mut self, item: &'tcx hir::Item<'_>) {
212201
let name = match &item.kind {
213202
hir::ItemKind::Impl(impl_) => {
Lines changed: 116 additions & 0 deletions
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+
}
Lines changed: 146 additions & 0 deletions
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)