@@ -9,37 +9,31 @@ use pulldown_cmark::Event::{
9
9
} ;
10
10
use pulldown_cmark:: Tag :: { CodeBlock , Heading , Item , Link , Paragraph } ;
11
11
use pulldown_cmark:: { BrokenLink , CodeBlockKind , CowStr , Options } ;
12
- use rustc_ast:: ast:: { Async , Attribute , Fn , FnRetTy , ItemKind } ;
12
+ use rustc_ast:: ast:: Attribute ;
13
13
use rustc_ast:: token:: CommentKind ;
14
14
use rustc_ast:: { AttrKind , AttrStyle } ;
15
15
use rustc_data_structures:: fx:: FxHashSet ;
16
- use rustc_data_structures:: sync:: Lrc ;
17
- use rustc_errors:: emitter:: EmitterWriter ;
18
- use rustc_errors:: { Applicability , Handler } ;
16
+ use rustc_errors:: Applicability ;
19
17
use rustc_hir as hir;
20
18
use rustc_hir:: intravisit:: { self , Visitor } ;
21
19
use rustc_hir:: { AnonConst , Expr } ;
22
20
use rustc_lint:: { LateContext , LateLintPass } ;
23
21
use rustc_middle:: hir:: nested_filter;
24
22
use rustc_middle:: lint:: in_external_macro;
25
23
use rustc_middle:: ty;
26
- use rustc_parse:: maybe_new_parser_from_source_str;
27
- use rustc_parse:: parser:: ForceCollect ;
28
24
use rustc_resolve:: rustdoc:: {
29
25
add_doc_fragment, attrs_to_doc_fragments, main_body_opts, source_span_for_markdown_range, DocFragment ,
30
26
} ;
31
- use rustc_session:: parse:: ParseSess ;
32
27
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
33
28
use rustc_span:: edition:: Edition ;
34
- use rustc_span:: source_map:: { FilePathMapping , SourceMap } ;
35
- use rustc_span:: { sym, FileName , Span } ;
29
+ use rustc_span:: { sym, Span } ;
36
30
use std:: ops:: Range ;
37
- use std:: { io, thread} ;
38
31
use url:: Url ;
39
32
40
33
mod link_with_quotes;
41
34
mod markdown;
42
35
mod missing_headers;
36
+ mod needless_doctest_main;
43
37
44
38
declare_clippy_lint ! {
45
39
/// ### What it does
@@ -624,7 +618,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
624
618
if in_code {
625
619
if is_rust && !no_test {
626
620
let edition = edition. unwrap_or_else ( || cx. tcx . sess . edition ( ) ) ;
627
- check_code ( cx, & text, edition, range. clone ( ) , fragments) ;
621
+ needless_doctest_main :: check ( cx, & text, edition, range. clone ( ) , fragments) ;
628
622
}
629
623
} else {
630
624
if in_link. is_some ( ) {
@@ -645,88 +639,6 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
645
639
headers
646
640
}
647
641
648
- fn check_code ( cx : & LateContext < ' _ > , text : & str , edition : Edition , range : Range < usize > , fragments : Fragments < ' _ > ) {
649
- fn has_needless_main ( code : String , edition : Edition ) -> bool {
650
- rustc_driver:: catch_fatal_errors ( || {
651
- rustc_span:: create_session_globals_then ( edition, || {
652
- let filename = FileName :: anon_source_code ( & code) ;
653
-
654
- let fallback_bundle =
655
- rustc_errors:: fallback_fluent_bundle ( rustc_driver:: DEFAULT_LOCALE_RESOURCES . to_vec ( ) , false ) ;
656
- let emitter = EmitterWriter :: new ( Box :: new ( io:: sink ( ) ) , fallback_bundle) ;
657
- let handler = Handler :: with_emitter ( Box :: new ( emitter) ) . disable_warnings ( ) ;
658
- #[ expect( clippy:: arc_with_non_send_sync) ] // `Lrc` is expected by with_span_handler
659
- let sm = Lrc :: new ( SourceMap :: new ( FilePathMapping :: empty ( ) ) ) ;
660
- let sess = ParseSess :: with_span_handler ( handler, sm) ;
661
-
662
- let mut parser = match maybe_new_parser_from_source_str ( & sess, filename, code) {
663
- Ok ( p) => p,
664
- Err ( errs) => {
665
- drop ( errs) ;
666
- return false ;
667
- } ,
668
- } ;
669
-
670
- let mut relevant_main_found = false ;
671
- loop {
672
- match parser. parse_item ( ForceCollect :: No ) {
673
- Ok ( Some ( item) ) => match & item. kind {
674
- ItemKind :: Fn ( box Fn {
675
- sig, body : Some ( block) , ..
676
- } ) if item. ident . name == sym:: main => {
677
- let is_async = matches ! ( sig. header. asyncness, Async :: Yes { .. } ) ;
678
- let returns_nothing = match & sig. decl . output {
679
- FnRetTy :: Default ( ..) => true ,
680
- FnRetTy :: Ty ( ty) if ty. kind . is_unit ( ) => true ,
681
- FnRetTy :: Ty ( _) => false ,
682
- } ;
683
-
684
- if returns_nothing && !is_async && !block. stmts . is_empty ( ) {
685
- // This main function should be linted, but only if there are no other functions
686
- relevant_main_found = true ;
687
- } else {
688
- // This main function should not be linted, we're done
689
- return false ;
690
- }
691
- } ,
692
- // Tests with one of these items are ignored
693
- ItemKind :: Static ( ..)
694
- | ItemKind :: Const ( ..)
695
- | ItemKind :: ExternCrate ( ..)
696
- | ItemKind :: ForeignMod ( ..)
697
- // Another function was found; this case is ignored
698
- | ItemKind :: Fn ( ..) => return false ,
699
- _ => { } ,
700
- } ,
701
- Ok ( None ) => break ,
702
- Err ( e) => {
703
- e. cancel ( ) ;
704
- return false ;
705
- } ,
706
- }
707
- }
708
-
709
- relevant_main_found
710
- } )
711
- } )
712
- . ok ( )
713
- . unwrap_or_default ( )
714
- }
715
-
716
- let trailing_whitespace = text. len ( ) - text. trim_end ( ) . len ( ) ;
717
-
718
- // Because of the global session, we need to create a new session in a different thread with
719
- // the edition we need.
720
- let text = text. to_owned ( ) ;
721
- if thread:: spawn ( move || has_needless_main ( text, edition) )
722
- . join ( )
723
- . expect ( "thread::spawn failed" )
724
- && let Some ( span) = fragments. span ( cx, range. start ..range. end - trailing_whitespace)
725
- {
726
- span_lint ( cx, NEEDLESS_DOCTEST_MAIN , span, "needless `fn main` in doctest" ) ;
727
- }
728
- }
729
-
730
642
struct FindPanicUnwrap < ' a , ' tcx > {
731
643
cx : & ' a LateContext < ' tcx > ,
732
644
panic_span : Option < Span > ,
0 commit comments