@@ -395,11 +395,36 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
395395 headers
396396}
397397
398- static LEAVE_MAIN_PATTERNS : & [ & str ] = & [ "static" , "fn main() {}" , "extern crate" ] ;
398+ fn needs_main ( item : & syn:: Item ) -> bool {
399+ match item {
400+ syn:: Item :: Const ( ..) | syn:: Item :: Static ( ..) | syn:: Item :: ExternCrate ( ..) | syn:: Item :: ForeignMod ( ..) => true ,
401+ _ => false ,
402+ }
403+ }
399404
400- fn check_code ( cx : & LateContext < ' _ , ' _ > , text : & str , span : Span ) {
401- if text. contains ( "fn main() {" ) && !LEAVE_MAIN_PATTERNS . iter ( ) . any ( |p| text. contains ( p) ) {
402- span_lint ( cx, NEEDLESS_DOCTEST_MAIN , span, "needless `fn main` in doctest" ) ;
405+ // check a syn Item for non-empty `fn main() { .. }`
406+ fn is_default_main_fn ( item : & syn:: Item ) -> bool {
407+ match item {
408+ syn:: Item :: Fn ( syn:: ItemFn { ref sig, ref block, .. } ) => {
409+ !block. stmts . is_empty ( )
410+ && sig. ident == "main"
411+ && match sig. output {
412+ syn:: ReturnType :: Default => true ,
413+ syn:: ReturnType :: Type ( _, ref ty) => match * * ty {
414+ syn:: Type :: Tuple ( syn:: TypeTuple { ref elems, .. } ) => elems. is_empty ( ) ,
415+ _ => false ,
416+ } ,
417+ }
418+ } ,
419+ _ => false ,
420+ }
421+ }
422+
423+ fn check_code ( cx : & LateContext < ' _ , ' _ > , code : & str , span : Span ) {
424+ if let Ok ( file) = syn:: parse_file ( code) {
425+ if file. items . iter ( ) . any ( is_default_main_fn) && !file. items . iter ( ) . any ( needs_main) {
426+ span_lint ( cx, NEEDLESS_DOCTEST_MAIN , span, "needless `fn main` in doctest" ) ;
427+ }
403428 }
404429}
405430
0 commit comments