@@ -402,30 +402,63 @@ pub fn make_test(s: &str,
402
402
// are intended to be crate attributes.
403
403
prog. push_str ( & crate_attrs) ;
404
404
405
+ // Uses libsyntax to parse the doctest and find if there's a main fn and the extern
406
+ // crate already is included.
407
+ let ( already_has_main, already_has_extern_crate) = crate :: syntax:: with_globals ( || {
408
+ use crate :: syntax:: { ast, parse:: { self , ParseSess } , source_map:: FilePathMapping } ;
409
+ use crate :: syntax_pos:: FileName ;
410
+
411
+ let filename = FileName :: Anon ;
412
+ let source = s. to_owned ( ) ;
413
+ let sess = ParseSess :: new ( FilePathMapping :: empty ( ) ) ;
414
+
415
+ let mut parser = parse:: new_parser_from_source_str ( & sess, filename, source) ;
416
+
417
+ let mut found_main = false ;
418
+ let mut found_extern_crate = cratename. is_none ( ) ;
419
+
420
+ while let Ok ( Some ( item) ) = parser. parse_item ( ) {
421
+ if !found_main {
422
+ if let ast:: ItemKind :: Fn ( ..) = item. node {
423
+ if item. ident . as_str ( ) == "main" {
424
+ found_main = true ;
425
+ }
426
+ }
427
+ }
428
+
429
+ if !found_extern_crate {
430
+ if let ast:: ItemKind :: ExternCrate ( original) = item. node {
431
+ // This code will never be reached if `cratename` is none ecause
432
+ // `found_extern_crate` is initialized to `true` if it is none.
433
+ let cratename = cratename. unwrap ( ) ;
434
+
435
+ match original {
436
+ Some ( name) => found_extern_crate = name. as_str ( ) == cratename,
437
+ None => found_extern_crate = item. ident . as_str ( ) == cratename,
438
+ }
439
+ }
440
+ }
441
+
442
+ if found_main && found_extern_crate {
443
+ break ;
444
+ }
445
+ }
446
+
447
+ ( found_main, found_extern_crate)
448
+ } ) ;
449
+
405
450
// Don't inject `extern crate std` because it's already injected by the
406
451
// compiler.
407
- if !s . contains ( "extern crate" ) && !opts. no_crate_inject && cratename != Some ( "std" ) {
452
+ if !already_has_extern_crate && !opts. no_crate_inject && cratename != Some ( "std" ) {
408
453
if let Some ( cratename) = cratename {
454
+ // Make sure its actually used if not included.
409
455
if s. contains ( cratename) {
410
456
prog. push_str ( & format ! ( "extern crate {};\n " , cratename) ) ;
411
457
line_offset += 1 ;
412
458
}
413
459
}
414
460
}
415
461
416
- // FIXME (#21299): prefer libsyntax or some other actual parser over this
417
- // best-effort ad hoc approach
418
- let already_has_main = s. lines ( )
419
- . map ( |line| {
420
- let comment = line. find ( "//" ) ;
421
- if let Some ( comment_begins) = comment {
422
- & line[ 0 ..comment_begins]
423
- } else {
424
- line
425
- }
426
- } )
427
- . any ( |code| code. contains ( "fn main" ) ) ;
428
-
429
462
if dont_insert_main || already_has_main {
430
463
prog. push_str ( everything_else) ;
431
464
} else {
@@ -1014,4 +1047,38 @@ assert_eq!(2+2, 4);
1014
1047
let output = make_test ( input, None , false , & opts) ;
1015
1048
assert_eq ! ( output, ( expected, 1 ) ) ;
1016
1049
}
1050
+
1051
+ #[ test]
1052
+ fn make_test_issues_21299_33731 ( ) {
1053
+ let opts = TestOptions :: default ( ) ;
1054
+
1055
+ let input =
1056
+ "// fn main
1057
+ assert_eq!(2+2, 4);" ;
1058
+
1059
+ let expected =
1060
+ "#![allow(unused)]
1061
+ fn main() {
1062
+ // fn main
1063
+ assert_eq!(2+2, 4);
1064
+ }" . to_string ( ) ;
1065
+
1066
+ let output = make_test ( input, None , false , & opts) ;
1067
+ assert_eq ! ( output, ( expected, 2 ) ) ;
1068
+
1069
+ let input =
1070
+ "extern crate hella_qwop;
1071
+ assert_eq!(asdf::foo, 4);" ;
1072
+
1073
+ let expected =
1074
+ "#![allow(unused)]
1075
+ extern crate hella_qwop;
1076
+ extern crate asdf;
1077
+ fn main() {
1078
+ assert_eq!(asdf::foo, 4);
1079
+ }" . to_string ( ) ;
1080
+
1081
+ let output = make_test ( input, Some ( "asdf" ) , false , & opts) ;
1082
+ assert_eq ! ( output, ( expected, 3 ) ) ;
1083
+ }
1017
1084
}
0 commit comments