Skip to content

Commit b79fdb8

Browse files
repnopQuietMisdreavus
authored andcommitted
Replaces fn main search and extern crate search with proper parsing.
1 parent d586d5d commit b79fdb8

File tree

1 file changed

+81
-14
lines changed

1 file changed

+81
-14
lines changed

src/librustdoc/test.rs

+81-14
Original file line numberDiff line numberDiff line change
@@ -402,30 +402,63 @@ pub fn make_test(s: &str,
402402
// are intended to be crate attributes.
403403
prog.push_str(&crate_attrs);
404404

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+
405450
// Don't inject `extern crate std` because it's already injected by the
406451
// 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") {
408453
if let Some(cratename) = cratename {
454+
// Make sure its actually used if not included.
409455
if s.contains(cratename) {
410456
prog.push_str(&format!("extern crate {};\n", cratename));
411457
line_offset += 1;
412458
}
413459
}
414460
}
415461

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-
429462
if dont_insert_main || already_has_main {
430463
prog.push_str(everything_else);
431464
} else {
@@ -1014,4 +1047,38 @@ assert_eq!(2+2, 4);
10141047
let output = make_test(input, None, false, &opts);
10151048
assert_eq!(output, (expected, 1));
10161049
}
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+
}
10171084
}

0 commit comments

Comments
 (0)