@@ -14,6 +14,7 @@ use rustc_middle::ty;
1414use rustc_parse:: maybe_new_parser_from_source_str;
1515use rustc_session:: parse:: ParseSess ;
1616use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
17+ use rustc_span:: edition:: Edition ;
1718use rustc_span:: source_map:: { BytePos , FilePathMapping , MultiSpan , SourceMap , Span } ;
1819use rustc_span:: { sym, FileName , Pos } ;
1920use std:: io;
@@ -377,7 +378,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs
377378 check_doc ( cx, valid_idents, events, & spans)
378379}
379380
380- const RUST_CODE : & [ & str ] = & [ "rust" , "no_run" , "should_panic" , "compile_fail" , "edition2018" ] ;
381+ const RUST_CODE : & [ & str ] = & [ "rust" , "no_run" , "should_panic" , "compile_fail" ] ;
381382
382383fn check_doc < ' a , Events : Iterator < Item = ( pulldown_cmark:: Event < ' a > , Range < usize > ) > > (
383384 cx : & LateContext < ' _ > ,
@@ -400,13 +401,24 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
400401 let mut in_link = None ;
401402 let mut in_heading = false ;
402403 let mut is_rust = false ;
404+ let mut edition = None ;
403405 for ( event, range) in events {
404406 match event {
405407 Start ( CodeBlock ( ref kind) ) => {
406408 in_code = true ;
407409 if let CodeBlockKind :: Fenced ( lang) = kind {
408- is_rust =
409- lang. is_empty ( ) || !lang. contains ( "ignore" ) && lang. split ( ',' ) . any ( |i| RUST_CODE . contains ( & i) ) ;
410+ for item in lang. split ( ',' ) {
411+ if item == "ignore" {
412+ is_rust = false ;
413+ break ;
414+ }
415+ if let Some ( stripped) = item. strip_prefix ( "edition" ) {
416+ is_rust = true ;
417+ edition = stripped. parse :: < Edition > ( ) . ok ( ) ;
418+ } else if item. is_empty ( ) || RUST_CODE . contains ( & item) {
419+ is_rust = true ;
420+ }
421+ }
410422 }
411423 } ,
412424 End ( CodeBlock ( _) ) => {
@@ -436,7 +448,8 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
436448 let ( begin, span) = spans[ index] ;
437449 if in_code {
438450 if is_rust {
439- check_code ( cx, & text, span) ;
451+ let edition = edition. unwrap_or_else ( || cx. tcx . sess . edition ( ) ) ;
452+ check_code ( cx, & text, edition, span) ;
440453 }
441454 } else {
442455 // Adjust for the beginning of the current `Event`
@@ -450,67 +463,73 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
450463 headers
451464}
452465
453- fn check_code ( cx : & LateContext < ' _ > , text : & str , span : Span ) {
454- fn has_needless_main ( code : & str ) -> bool {
455- let filename = FileName :: anon_source_code ( code) ;
456-
457- let sm = Lrc :: new ( SourceMap :: new ( FilePathMapping :: empty ( ) ) ) ;
458- let emitter = EmitterWriter :: new ( box io:: sink ( ) , None , false , false , false , None , false ) ;
459- let handler = Handler :: with_emitter ( false , None , box emitter) ;
460- let sess = ParseSess :: with_span_handler ( handler, sm) ;
461-
462- let mut parser = match maybe_new_parser_from_source_str ( & sess, filename, code. into ( ) ) {
463- Ok ( p) => p,
464- Err ( errs) => {
465- for mut err in errs {
466- err. cancel ( ) ;
467- }
468- return false ;
469- } ,
470- } ;
471-
472- let mut relevant_main_found = false ;
473- loop {
474- match parser. parse_item ( ) {
475- Ok ( Some ( item) ) => match & item. kind {
476- // Tests with one of these items are ignored
477- ItemKind :: Static ( ..)
478- | ItemKind :: Const ( ..)
479- | ItemKind :: ExternCrate ( ..)
480- | ItemKind :: ForeignMod ( ..) => return false ,
481- // We found a main function ...
482- ItemKind :: Fn ( _, sig, _, Some ( block) ) if item. ident . name == sym:: main => {
483- let is_async = matches ! ( sig. header. asyncness, Async :: Yes { .. } ) ;
484- let returns_nothing = match & sig. decl . output {
485- FnRetTy :: Default ( ..) => true ,
486- FnRetTy :: Ty ( ty) if ty. kind . is_unit ( ) => true ,
487- _ => false ,
488- } ;
489-
490- if returns_nothing && !is_async && !block. stmts . is_empty ( ) {
491- // This main function should be linted, but only if there are no other functions
492- relevant_main_found = true ;
493- } else {
494- // This main function should not be linted, we're done
495- return false ;
466+ fn check_code ( cx : & LateContext < ' _ > , text : & str , edition : Edition , span : Span ) {
467+ fn has_needless_main ( code : & str , edition : Edition ) -> bool {
468+ rustc_driver:: catch_fatal_errors ( || {
469+ rustc_span:: with_session_globals ( edition, || {
470+ let filename = FileName :: anon_source_code ( code) ;
471+
472+ let sm = Lrc :: new ( SourceMap :: new ( FilePathMapping :: empty ( ) ) ) ;
473+ let emitter = EmitterWriter :: new ( box io:: sink ( ) , None , false , false , false , None , false ) ;
474+ let handler = Handler :: with_emitter ( false , None , box emitter) ;
475+ let sess = ParseSess :: with_span_handler ( handler, sm) ;
476+
477+ let mut parser = match maybe_new_parser_from_source_str ( & sess, filename, code. into ( ) ) {
478+ Ok ( p) => p,
479+ Err ( errs) => {
480+ for mut err in errs {
481+ err. cancel ( ) ;
496482 }
483+ return false ;
497484 } ,
498- // Another function was found; this case is ignored too
499- ItemKind :: Fn ( ..) => return false ,
500- _ => { } ,
501- } ,
502- Ok ( None ) => break ,
503- Err ( mut e) => {
504- e. cancel ( ) ;
505- return false ;
506- } ,
507- }
508- }
485+ } ;
486+
487+ let mut relevant_main_found = false ;
488+ loop {
489+ match parser. parse_item ( ) {
490+ Ok ( Some ( item) ) => match & item. kind {
491+ // Tests with one of these items are ignored
492+ ItemKind :: Static ( ..)
493+ | ItemKind :: Const ( ..)
494+ | ItemKind :: ExternCrate ( ..)
495+ | ItemKind :: ForeignMod ( ..) => return false ,
496+ // We found a main function ...
497+ ItemKind :: Fn ( _, sig, _, Some ( block) ) if item. ident . name == sym:: main => {
498+ let is_async = matches ! ( sig. header. asyncness, Async :: Yes { .. } ) ;
499+ let returns_nothing = match & sig. decl . output {
500+ FnRetTy :: Default ( ..) => true ,
501+ FnRetTy :: Ty ( ty) if ty. kind . is_unit ( ) => true ,
502+ _ => false ,
503+ } ;
504+
505+ if returns_nothing && !is_async && !block. stmts . is_empty ( ) {
506+ // This main function should be linted, but only if there are no other functions
507+ relevant_main_found = true ;
508+ } else {
509+ // This main function should not be linted, we're done
510+ return false ;
511+ }
512+ } ,
513+ // Another function was found; this case is ignored too
514+ ItemKind :: Fn ( ..) => return false ,
515+ _ => { } ,
516+ } ,
517+ Ok ( None ) => break ,
518+ Err ( mut e) => {
519+ e. cancel ( ) ;
520+ return false ;
521+ } ,
522+ }
523+ }
509524
510- relevant_main_found
525+ relevant_main_found
526+ } )
527+ } )
528+ . ok ( )
529+ . unwrap_or_default ( )
511530 }
512531
513- if has_needless_main ( text) {
532+ if has_needless_main ( text, edition ) {
514533 span_lint ( cx, NEEDLESS_DOCTEST_MAIN , span, "needless `fn main` in doctest" ) ;
515534 }
516535}
0 commit comments