@@ -8,14 +8,14 @@ use std::sync::Arc;
88use rustc_ast:: token:: { Delimiter , TokenKind } ;
99use rustc_ast:: tokenstream:: TokenTree ;
1010use rustc_ast:: { self as ast, AttrStyle , HasAttrs , StmtKind } ;
11- use rustc_errors:: ColorConfig ;
1211use rustc_errors:: emitter:: stderr_destination;
12+ use rustc_errors:: { ColorConfig , DiagCtxtHandle } ;
1313use rustc_parse:: new_parser_from_source_str;
1414use rustc_session:: parse:: ParseSess ;
15- use rustc_span:: edition:: Edition ;
15+ use rustc_span:: edition:: { DEFAULT_EDITION , Edition } ;
1616use rustc_span:: source_map:: SourceMap ;
1717use rustc_span:: symbol:: sym;
18- use rustc_span:: { FileName , kw} ;
18+ use rustc_span:: { DUMMY_SP , FileName , Span , kw} ;
1919use tracing:: debug;
2020
2121use super :: GlobalTestOptions ;
@@ -35,41 +35,86 @@ struct ParseSourceInfo {
3535 maybe_crate_attrs : String ,
3636}
3737
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 ,
5348}
5449
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 ;
65110 let can_merge_doctests = can_merge_doctests
66111 && lang_str. is_some_and ( |lang_str| {
67112 !lang_str. compile_fail && !lang_str. test_harness && !lang_str. standalone_crate
68113 } ) ;
69114
70115 let result = rustc_driver:: catch_fatal_errors ( || {
71116 rustc_span:: create_session_if_not_set_then ( edition, |_| {
72- parse_source ( source, & crate_name)
117+ parse_source ( source, & crate_name, dcx , span )
73118 } )
74119 } ) ;
75120
@@ -87,7 +132,7 @@ impl DocTestBuilder {
87132 else {
88133 // If the AST returned an error, we don't want this doctest to be merged with the
89134 // others.
90- return Self :: invalid (
135+ return DocTestBuilder :: invalid (
91136 String :: new ( ) ,
92137 String :: new ( ) ,
93138 String :: new ( ) ,
@@ -107,7 +152,7 @@ impl DocTestBuilder {
107152 // If this is a merged doctest and a defined macro uses `$crate`, then the path will
108153 // not work, so better not put it into merged doctests.
109154 && !( has_macro_def && everything_else. contains ( "$crate" ) ) ;
110- Self {
155+ DocTestBuilder {
111156 supports_color,
112157 has_main_fn,
113158 crate_attrs,
@@ -120,7 +165,26 @@ impl DocTestBuilder {
120165 can_be_merged,
121166 }
122167 }
168+ }
123169
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 {
124188 fn invalid (
125189 crate_attrs : String ,
126190 maybe_crate_attrs : String ,
@@ -289,7 +353,12 @@ fn reset_error_count(psess: &ParseSess) {
289353
290354const DOCTEST_CODE_WRAPPER : & str = "fn f(){" ;
291355
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 , ( ) > {
293362 use rustc_errors:: DiagCtxt ;
294363 use rustc_errors:: emitter:: { Emitter , HumanEmitter } ;
295364 use rustc_span:: source_map:: FilePathMapping ;
@@ -466,8 +535,17 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn
466535 }
467536 }
468537 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+ }
471549 info. has_main_fn = false ;
472550 }
473551 Ok ( info)
0 commit comments