@@ -395,11 +395,36 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
395
395
headers
396
396
}
397
397
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
+ }
399
404
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
+ }
403
428
}
404
429
}
405
430
0 commit comments