@@ -8,14 +8,14 @@ use std::sync::Arc;
8
8
use rustc_ast:: token:: { Delimiter , TokenKind } ;
9
9
use rustc_ast:: tokenstream:: TokenTree ;
10
10
use rustc_ast:: { self as ast, AttrStyle , HasAttrs , StmtKind } ;
11
- use rustc_errors:: ColorConfig ;
12
11
use rustc_errors:: emitter:: stderr_destination;
12
+ use rustc_errors:: { ColorConfig , DiagCtxtHandle } ;
13
13
use rustc_parse:: new_parser_from_source_str;
14
14
use rustc_session:: parse:: ParseSess ;
15
- use rustc_span:: edition:: Edition ;
15
+ use rustc_span:: edition:: { DEFAULT_EDITION , Edition } ;
16
16
use rustc_span:: source_map:: SourceMap ;
17
17
use rustc_span:: symbol:: sym;
18
- use rustc_span:: { FileName , kw} ;
18
+ use rustc_span:: { DUMMY_SP , FileName , Span , kw} ;
19
19
use tracing:: debug;
20
20
21
21
use super :: GlobalTestOptions ;
@@ -35,41 +35,86 @@ struct ParseSourceInfo {
35
35
maybe_crate_attrs : String ,
36
36
}
37
37
38
- /// This struct contains information about the doctest itself which is then used to generate
39
- /// doctest source code appropriately.
40
- pub ( crate ) struct DocTestBuilder {
41
- pub ( crate ) supports_color : bool ,
42
- pub ( crate ) already_has_extern_crate : bool ,
43
- pub ( crate ) has_main_fn : bool ,
44
- pub ( crate ) crate_attrs : String ,
45
- /// If this is a merged doctest, it will be put into `everything_else`, otherwise it will
46
- /// put into `crate_attrs`.
47
- pub ( crate ) maybe_crate_attrs : String ,
48
- pub ( crate ) crates : String ,
49
- pub ( crate ) everything_else : String ,
50
- pub ( crate ) test_id : Option < String > ,
51
- pub ( crate ) invalid_ast : bool ,
52
- pub ( crate ) can_be_merged : bool ,
38
+ /// Builder type for `DocTestBuilder`.
39
+ pub ( crate ) struct BuildDocTestBuilder < ' a > {
40
+ source : & ' a str ,
41
+ crate_name : Option < & ' a str > ,
42
+ edition : Edition ,
43
+ can_merge_doctests : bool ,
44
+ // If `test_id` is `None`, it means we're generating code for a code example "run" link.
45
+ test_id : Option < String > ,
46
+ lang_str : Option < & ' a LangString > ,
47
+ span : Span ,
53
48
}
54
49
55
- impl DocTestBuilder {
56
- pub ( crate ) fn new (
57
- source : & str ,
58
- crate_name : Option < & str > ,
59
- edition : Edition ,
60
- can_merge_doctests : bool ,
61
- // If `test_id` is `None`, it means we're generating code for a code example "run" link.
62
- test_id : Option < String > ,
63
- lang_str : Option < & LangString > ,
64
- ) -> Self {
50
+ impl < ' a > BuildDocTestBuilder < ' a > {
51
+ pub ( crate ) fn new ( source : & ' a str ) -> Self {
52
+ Self {
53
+ source,
54
+ crate_name : None ,
55
+ edition : DEFAULT_EDITION ,
56
+ can_merge_doctests : false ,
57
+ test_id : None ,
58
+ lang_str : None ,
59
+ span : DUMMY_SP ,
60
+ }
61
+ }
62
+
63
+ #[ inline]
64
+ pub ( crate ) fn crate_name ( mut self , crate_name : & ' a str ) -> Self {
65
+ self . crate_name = Some ( crate_name) ;
66
+ self
67
+ }
68
+
69
+ #[ inline]
70
+ pub ( crate ) fn can_merge_doctests ( mut self , can_merge_doctests : bool ) -> Self {
71
+ self . can_merge_doctests = can_merge_doctests;
72
+ self
73
+ }
74
+
75
+ #[ inline]
76
+ pub ( crate ) fn test_id ( mut self , test_id : String ) -> Self {
77
+ self . test_id = Some ( test_id) ;
78
+ self
79
+ }
80
+
81
+ #[ inline]
82
+ pub ( crate ) fn lang_str ( mut self , lang_str : & ' a LangString ) -> Self {
83
+ self . lang_str = Some ( lang_str) ;
84
+ self
85
+ }
86
+
87
+ #[ inline]
88
+ pub ( crate ) fn span ( mut self , span : Span ) -> Self {
89
+ self . span = span;
90
+ self
91
+ }
92
+
93
+ #[ inline]
94
+ pub ( crate ) fn edition ( mut self , edition : Edition ) -> Self {
95
+ self . edition = edition;
96
+ self
97
+ }
98
+
99
+ pub ( crate ) fn build ( self , dcx : Option < DiagCtxtHandle < ' _ > > ) -> DocTestBuilder {
100
+ let BuildDocTestBuilder {
101
+ source,
102
+ crate_name,
103
+ edition,
104
+ can_merge_doctests,
105
+ // If `test_id` is `None`, it means we're generating code for a code example "run" link.
106
+ test_id,
107
+ lang_str,
108
+ span,
109
+ } = self ;
65
110
let can_merge_doctests = can_merge_doctests
66
111
&& lang_str. is_some_and ( |lang_str| {
67
112
!lang_str. compile_fail && !lang_str. test_harness && !lang_str. standalone_crate
68
113
} ) ;
69
114
70
115
let result = rustc_driver:: catch_fatal_errors ( || {
71
116
rustc_span:: create_session_if_not_set_then ( edition, |_| {
72
- parse_source ( source, & crate_name)
117
+ parse_source ( source, & crate_name, dcx , span )
73
118
} )
74
119
} ) ;
75
120
@@ -87,7 +132,7 @@ impl DocTestBuilder {
87
132
else {
88
133
// If the AST returned an error, we don't want this doctest to be merged with the
89
134
// others.
90
- return Self :: invalid (
135
+ return DocTestBuilder :: invalid (
91
136
String :: new ( ) ,
92
137
String :: new ( ) ,
93
138
String :: new ( ) ,
@@ -107,7 +152,7 @@ impl DocTestBuilder {
107
152
// If this is a merged doctest and a defined macro uses `$crate`, then the path will
108
153
// not work, so better not put it into merged doctests.
109
154
&& !( has_macro_def && everything_else. contains ( "$crate" ) ) ;
110
- Self {
155
+ DocTestBuilder {
111
156
supports_color,
112
157
has_main_fn,
113
158
crate_attrs,
@@ -120,7 +165,26 @@ impl DocTestBuilder {
120
165
can_be_merged,
121
166
}
122
167
}
168
+ }
123
169
170
+ /// This struct contains information about the doctest itself which is then used to generate
171
+ /// doctest source code appropriately.
172
+ pub ( crate ) struct DocTestBuilder {
173
+ pub ( crate ) supports_color : bool ,
174
+ pub ( crate ) already_has_extern_crate : bool ,
175
+ pub ( crate ) has_main_fn : bool ,
176
+ pub ( crate ) crate_attrs : String ,
177
+ /// If this is a merged doctest, it will be put into `everything_else`, otherwise it will
178
+ /// put into `crate_attrs`.
179
+ pub ( crate ) maybe_crate_attrs : String ,
180
+ pub ( crate ) crates : String ,
181
+ pub ( crate ) everything_else : String ,
182
+ pub ( crate ) test_id : Option < String > ,
183
+ pub ( crate ) invalid_ast : bool ,
184
+ pub ( crate ) can_be_merged : bool ,
185
+ }
186
+
187
+ impl DocTestBuilder {
124
188
fn invalid (
125
189
crate_attrs : String ,
126
190
maybe_crate_attrs : String ,
@@ -289,7 +353,12 @@ fn reset_error_count(psess: &ParseSess) {
289
353
290
354
const DOCTEST_CODE_WRAPPER : & str = "fn f(){" ;
291
355
292
- fn parse_source ( source : & str , crate_name : & Option < & str > ) -> Result < ParseSourceInfo , ( ) > {
356
+ fn parse_source (
357
+ source : & str ,
358
+ crate_name : & Option < & str > ,
359
+ parent_dcx : Option < DiagCtxtHandle < ' _ > > ,
360
+ span : Span ,
361
+ ) -> Result < ParseSourceInfo , ( ) > {
293
362
use rustc_errors:: DiagCtxt ;
294
363
use rustc_errors:: emitter:: { Emitter , HumanEmitter } ;
295
364
use rustc_span:: source_map:: FilePathMapping ;
@@ -466,8 +535,17 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn
466
535
}
467
536
}
468
537
if has_non_items {
469
- // FIXME: if `info.has_main_fn` is `true`, emit a warning here to mention that
470
- // this code will not be called.
538
+ if info. has_main_fn
539
+ && let Some ( dcx) = parent_dcx
540
+ && !span. is_dummy ( )
541
+ {
542
+ dcx. span_warn (
543
+ span,
544
+ "the `main` function of this doctest won't be run as it contains \
545
+ expressions at the top level, meaning that the whole doctest code will be \
546
+ wrapped in a function",
547
+ ) ;
548
+ }
471
549
info. has_main_fn = false ;
472
550
}
473
551
Ok ( info)
0 commit comments