@@ -378,7 +378,7 @@ pub fn make_test(s: &str,
378
378
dont_insert_main : bool ,
379
379
opts : & TestOptions )
380
380
-> ( String , usize ) {
381
- let ( crate_attrs, everything_else) = partition_source ( s) ;
381
+ let ( crate_attrs, everything_else, crates ) = partition_source ( s) ;
382
382
let everything_else = everything_else. trim ( ) ;
383
383
let mut line_offset = 0 ;
384
384
let mut prog = String :: new ( ) ;
@@ -402,30 +402,91 @@ 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
+ use errors:: emitter:: EmitterWriter ;
411
+ use errors:: Handler ;
412
+
413
+ let filename = FileName :: Anon ;
414
+ let source = crates + & everything_else;
415
+
416
+ // any errors in parsing should also appear when the doctest is compiled for real, so just
417
+ // send all the errors that libsyntax emits directly into a Sink instead of stderr
418
+ let cm = Lrc :: new ( SourceMap :: new ( FilePathMapping :: empty ( ) ) ) ;
419
+ let emitter = EmitterWriter :: new ( box io:: sink ( ) , None , false , false ) ;
420
+ let handler = Handler :: with_emitter ( false , false , box emitter) ;
421
+ let sess = ParseSess :: with_span_handler ( handler, cm) ;
422
+
423
+ debug ! ( "about to parse: \n {}" , source) ;
424
+
425
+ let mut found_main = false ;
426
+ let mut found_extern_crate = cratename. is_none ( ) ;
427
+
428
+ let mut parser = match parse:: maybe_new_parser_from_source_str ( & sess, filename, source) {
429
+ Ok ( p) => p,
430
+ Err ( errs) => {
431
+ for mut err in errs {
432
+ err. cancel ( ) ;
433
+ }
434
+
435
+ return ( found_main, found_extern_crate) ;
436
+ }
437
+ } ;
438
+
439
+ loop {
440
+ match parser. parse_item ( ) {
441
+ Ok ( Some ( item) ) => {
442
+ if !found_main {
443
+ if let ast:: ItemKind :: Fn ( ..) = item. node {
444
+ if item. ident . as_str ( ) == "main" {
445
+ found_main = true ;
446
+ }
447
+ }
448
+ }
449
+
450
+ if !found_extern_crate {
451
+ if let ast:: ItemKind :: ExternCrate ( original) = item. node {
452
+ // This code will never be reached if `cratename` is none because
453
+ // `found_extern_crate` is initialized to `true` if it is none.
454
+ let cratename = cratename. unwrap ( ) ;
455
+
456
+ match original {
457
+ Some ( name) => found_extern_crate = name. as_str ( ) == cratename,
458
+ None => found_extern_crate = item. ident . as_str ( ) == cratename,
459
+ }
460
+ }
461
+ }
462
+
463
+ if found_main && found_extern_crate {
464
+ break ;
465
+ }
466
+ }
467
+ Ok ( None ) => break ,
468
+ Err ( mut e) => {
469
+ e. cancel ( ) ;
470
+ break ;
471
+ }
472
+ }
473
+ }
474
+
475
+ ( found_main, found_extern_crate)
476
+ } ) ;
477
+
405
478
// Don't inject `extern crate std` because it's already injected by the
406
479
// compiler.
407
- if !s . contains ( "extern crate" ) && !opts. no_crate_inject && cratename != Some ( "std" ) {
480
+ if !already_has_extern_crate && !opts. no_crate_inject && cratename != Some ( "std" ) {
408
481
if let Some ( cratename) = cratename {
482
+ // Make sure its actually used if not included.
409
483
if s. contains ( cratename) {
410
484
prog. push_str ( & format ! ( "extern crate {};\n " , cratename) ) ;
411
485
line_offset += 1 ;
412
486
}
413
487
}
414
488
}
415
489
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
490
if dont_insert_main || already_has_main {
430
491
prog. push_str ( everything_else) ;
431
492
} else {
@@ -441,9 +502,10 @@ pub fn make_test(s: &str,
441
502
}
442
503
443
504
// FIXME(aburka): use a real parser to deal with multiline attributes
444
- fn partition_source ( s : & str ) -> ( String , String ) {
505
+ fn partition_source ( s : & str ) -> ( String , String , String ) {
445
506
let mut after_header = false ;
446
507
let mut before = String :: new ( ) ;
508
+ let mut crates = String :: new ( ) ;
447
509
let mut after = String :: new ( ) ;
448
510
449
511
for line in s. lines ( ) {
@@ -457,12 +519,17 @@ fn partition_source(s: &str) -> (String, String) {
457
519
after. push_str ( line) ;
458
520
after. push_str ( "\n " ) ;
459
521
} else {
522
+ if trimline. starts_with ( "#[macro_use] extern crate" )
523
+ || trimline. starts_with ( "extern crate" ) {
524
+ crates. push_str ( line) ;
525
+ crates. push_str ( "\n " ) ;
526
+ }
460
527
before. push_str ( line) ;
461
528
before. push_str ( "\n " ) ;
462
529
}
463
530
}
464
531
465
- ( before, after)
532
+ ( before, after, crates )
466
533
}
467
534
468
535
pub trait Tester {
@@ -1014,4 +1081,38 @@ assert_eq!(2+2, 4);
1014
1081
let output = make_test ( input, None , false , & opts) ;
1015
1082
assert_eq ! ( output, ( expected, 1 ) ) ;
1016
1083
}
1084
+
1085
+ #[ test]
1086
+ fn make_test_issues_21299_33731 ( ) {
1087
+ let opts = TestOptions :: default ( ) ;
1088
+
1089
+ let input =
1090
+ "// fn main
1091
+ assert_eq!(2+2, 4);" ;
1092
+
1093
+ let expected =
1094
+ "#![allow(unused)]
1095
+ fn main() {
1096
+ // fn main
1097
+ assert_eq!(2+2, 4);
1098
+ }" . to_string ( ) ;
1099
+
1100
+ let output = make_test ( input, None , false , & opts) ;
1101
+ assert_eq ! ( output, ( expected, 2 ) ) ;
1102
+
1103
+ let input =
1104
+ "extern crate hella_qwop;
1105
+ assert_eq!(asdf::foo, 4);" ;
1106
+
1107
+ let expected =
1108
+ "#![allow(unused)]
1109
+ extern crate hella_qwop;
1110
+ extern crate asdf;
1111
+ fn main() {
1112
+ assert_eq!(asdf::foo, 4);
1113
+ }" . to_string ( ) ;
1114
+
1115
+ let output = make_test ( input, Some ( "asdf" ) , false , & opts) ;
1116
+ assert_eq ! ( output, ( expected, 3 ) ) ;
1117
+ }
1017
1118
}
0 commit comments