Skip to content

Commit 6afcb56

Browse files
committed
Auto merge of #60065 - QuietMisdreavus:async-move-doctests, r=ollie27
rustdoc: set the default edition when pre-parsing a doctest Fixes #59313 (possibly more? i think we've had issues with parsing edition-specific syntax in doctests at some point) When handling a doctest, rustdoc needs to parse it beforehand, so that it can see whether it declares a `fn main` or `extern crate my_crate` explicitly. However, while doing this, rustdoc doesn't set the "default edition" used by the parser like the regular compilation runs do. This caused a problem when parsing a doctest with an `async move` block in it, since it was expecting the `move` keyword to start a closure, not a block. This PR changes the `rustdoc::test::make_test` function to set the parser's default edition while looking for a main function and `extern crate` statement. However, to do this, `make_test` needs to know what edition to set. Since this is also used during the HTML rendering process (to make playground URLs), now the HTML renderer needs to know about the default edition. Upshot: rendering standalone markdown files can now accept a "default edition" for their doctests with the `--edition` flag! (I'm pretty sure i waffled around how to set that a long time ago when we first added the `--edition` flag... `>_>`) I'm posting this before i stop for the night so that i can write this description while it's still in my head, but before this merges i want to make sure that (1) the `rustdoc-ui/failed-doctest-output` test still works (i expect it doesn't), and (2) i add a test with the sample from the linked issue.
2 parents e7591c1 + 76b4900 commit 6afcb56

File tree

11 files changed

+136
-80
lines changed

11 files changed

+136
-80
lines changed

src/librustdoc/config.rs

+15-11
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rustc::session::config::{nightly_options, build_codegen_options, build_debug
1313
use rustc::session::search_paths::SearchPath;
1414
use rustc_driver;
1515
use rustc_target::spec::TargetTriple;
16-
use syntax::edition::Edition;
16+
use syntax::edition::{Edition, DEFAULT_EDITION};
1717

1818
use crate::core::new_handler;
1919
use crate::externalfiles::ExternalHtml;
@@ -386,27 +386,31 @@ impl Options {
386386
}
387387
}
388388

389+
let edition = if let Some(e) = matches.opt_str("edition") {
390+
match e.parse() {
391+
Ok(e) => e,
392+
Err(_) => {
393+
diag.struct_err("could not parse edition").emit();
394+
return Err(1);
395+
}
396+
}
397+
} else {
398+
DEFAULT_EDITION
399+
};
400+
389401
let mut id_map = html::markdown::IdMap::new();
390402
id_map.populate(html::render::initial_ids());
391403
let external_html = match ExternalHtml::load(
392404
&matches.opt_strs("html-in-header"),
393405
&matches.opt_strs("html-before-content"),
394406
&matches.opt_strs("html-after-content"),
395407
&matches.opt_strs("markdown-before-content"),
396-
&matches.opt_strs("markdown-after-content"), &diag, &mut id_map) {
408+
&matches.opt_strs("markdown-after-content"),
409+
&diag, &mut id_map, edition) {
397410
Some(eh) => eh,
398411
None => return Err(3),
399412
};
400413

401-
let edition = matches.opt_str("edition").unwrap_or("2015".to_string());
402-
let edition = match edition.parse() {
403-
Ok(e) => e,
404-
Err(_) => {
405-
diag.struct_err("could not parse edition").emit();
406-
return Err(1);
407-
}
408-
};
409-
410414
match matches.opt_str("r").as_ref().map(|s| &**s) {
411415
Some("rust") | None => {}
412416
Some(s) => {

src/librustdoc/externalfiles.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::path::Path;
33
use std::str;
44
use errors;
55
use crate::syntax::feature_gate::UnstableFeatures;
6+
use crate::syntax::edition::Edition;
67
use crate::html::markdown::{IdMap, ErrorCodes, Markdown};
78

89
use std::cell::RefCell;
@@ -23,7 +24,7 @@ pub struct ExternalHtml {
2324
impl ExternalHtml {
2425
pub fn load(in_header: &[String], before_content: &[String], after_content: &[String],
2526
md_before_content: &[String], md_after_content: &[String], diag: &errors::Handler,
26-
id_map: &mut IdMap)
27+
id_map: &mut IdMap, edition: Edition)
2728
-> Option<ExternalHtml> {
2829
let codes = ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build());
2930
load_external_files(in_header, diag)
@@ -34,7 +35,8 @@ impl ExternalHtml {
3435
.and_then(|(ih, bc)|
3536
load_external_files(md_before_content, diag)
3637
.map(|m_bc| (ih,
37-
format!("{}{}", bc, Markdown(&m_bc, &[], RefCell::new(id_map), codes))))
38+
format!("{}{}", bc, Markdown(&m_bc, &[], RefCell::new(id_map),
39+
codes, edition))))
3840
)
3941
.and_then(|(ih, bc)|
4042
load_external_files(after_content, diag)
@@ -43,7 +45,8 @@ impl ExternalHtml {
4345
.and_then(|(ih, bc, ac)|
4446
load_external_files(md_after_content, diag)
4547
.map(|m_ac| (ih, bc,
46-
format!("{}{}", ac, Markdown(&m_ac, &[], RefCell::new(id_map), codes))))
48+
format!("{}{}", ac, Markdown(&m_ac, &[], RefCell::new(id_map),
49+
codes, edition))))
4750
)
4851
.map(|(ih, bc, ac)|
4952
ExternalHtml {

src/librustdoc/html/markdown.rs

+41-30
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@
88
//! ```
99
//! #![feature(rustc_private)]
1010
//!
11+
//! extern crate syntax;
12+
//!
13+
//! use syntax::edition::Edition;
1114
//! use rustdoc::html::markdown::{IdMap, Markdown, ErrorCodes};
1215
//! use std::cell::RefCell;
1316
//!
1417
//! let s = "My *markdown* _text_";
1518
//! let mut id_map = IdMap::new();
16-
//! let html = format!("{}", Markdown(s, &[], RefCell::new(&mut id_map), ErrorCodes::Yes));
19+
//! let html = format!("{}", Markdown(s, &[], RefCell::new(&mut id_map),
20+
//! ErrorCodes::Yes, Edition::Edition2015));
1721
//! // ... something using html
1822
//! ```
1923
@@ -42,14 +46,21 @@ fn opts() -> Options {
4246
/// A unit struct which has the `fmt::Display` trait implemented. When
4347
/// formatted, this struct will emit the HTML corresponding to the rendered
4448
/// version of the contained markdown string.
45-
/// The second parameter is a list of link replacements
49+
///
50+
/// The second parameter is a list of link replacements.
51+
///
52+
/// The third is the current list of used header IDs.
53+
///
54+
/// The fourth is whether to allow the use of explicit error codes in doctest lang strings.
55+
///
56+
/// The fifth is what default edition to use when parsing doctests (to add a `fn main`).
4657
pub struct Markdown<'a>(
47-
pub &'a str, pub &'a [(String, String)], pub RefCell<&'a mut IdMap>, pub ErrorCodes);
58+
pub &'a str, pub &'a [(String, String)], pub RefCell<&'a mut IdMap>, pub ErrorCodes, pub Edition);
4859
/// A unit struct like `Markdown`, that renders the markdown with a
4960
/// table of contents.
50-
pub struct MarkdownWithToc<'a>(pub &'a str, pub RefCell<&'a mut IdMap>, pub ErrorCodes);
61+
pub struct MarkdownWithToc<'a>(pub &'a str, pub RefCell<&'a mut IdMap>, pub ErrorCodes, pub Edition);
5162
/// A unit struct like `Markdown`, that renders the markdown escaping HTML tags.
52-
pub struct MarkdownHtml<'a>(pub &'a str, pub RefCell<&'a mut IdMap>, pub ErrorCodes);
63+
pub struct MarkdownHtml<'a>(pub &'a str, pub RefCell<&'a mut IdMap>, pub ErrorCodes, pub Edition);
5364
/// A unit struct like `Markdown`, that renders only the first paragraph.
5465
pub struct MarkdownSummaryLine<'a>(pub &'a str, pub &'a [(String, String)]);
5566

@@ -146,13 +157,15 @@ thread_local!(pub static PLAYGROUND: RefCell<Option<(Option<String>, String)>> =
146157
struct CodeBlocks<'a, I: Iterator<Item = Event<'a>>> {
147158
inner: I,
148159
check_error_codes: ErrorCodes,
160+
edition: Edition,
149161
}
150162

151163
impl<'a, I: Iterator<Item = Event<'a>>> CodeBlocks<'a, I> {
152-
fn new(iter: I, error_codes: ErrorCodes) -> Self {
164+
fn new(iter: I, error_codes: ErrorCodes, edition: Edition) -> Self {
153165
CodeBlocks {
154166
inner: iter,
155167
check_error_codes: error_codes,
168+
edition,
156169
}
157170
}
158171
}
@@ -177,6 +190,9 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'a, I> {
177190
return event;
178191
}
179192

193+
let explicit_edition = edition.is_some();
194+
let edition = edition.unwrap_or(self.edition);
195+
180196
let mut origtext = String::new();
181197
for event in &mut self.inner {
182198
match event {
@@ -202,22 +218,14 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'a, I> {
202218
.collect::<Vec<Cow<'_, str>>>().join("\n");
203219
let krate = krate.as_ref().map(|s| &**s);
204220
let (test, _) = test::make_test(&test, krate, false,
205-
&Default::default());
221+
&Default::default(), edition);
206222
let channel = if test.contains("#![feature(") {
207223
"&amp;version=nightly"
208224
} else {
209225
""
210226
};
211227

212-
let edition_string = if let Some(e @ Edition::Edition2018) = edition {
213-
format!("&amp;edition={}{}", e,
214-
if channel == "&amp;version=nightly" { "" }
215-
else { "&amp;version=nightly" })
216-
} else if let Some(e) = edition {
217-
format!("&amp;edition={}", e)
218-
} else {
219-
"".to_owned()
220-
};
228+
let edition_string = format!("&amp;edition={}", edition);
221229

222230
// These characters don't need to be escaped in a URI.
223231
// FIXME: use a library function for percent encoding.
@@ -247,8 +255,8 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'a, I> {
247255
Some(("This example is not tested".to_owned(), "ignore"))
248256
} else if compile_fail {
249257
Some(("This example deliberately fails to compile".to_owned(), "compile_fail"))
250-
} else if let Some(e) = edition {
251-
Some((format!("This code runs with edition {}", e), "edition"))
258+
} else if explicit_edition {
259+
Some((format!("This code runs with edition {}", edition), "edition"))
252260
} else {
253261
None
254262
};
@@ -259,7 +267,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'a, I> {
259267
Some(&format!("rust-example-rendered{}",
260268
if ignore { " ignore" }
261269
else if compile_fail { " compile_fail" }
262-
else if edition.is_some() { " edition " }
270+
else if explicit_edition { " edition " }
263271
else { "" })),
264272
playground_button.as_ref().map(String::as_str),
265273
Some((s1.as_str(), s2))));
@@ -270,7 +278,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'a, I> {
270278
Some(&format!("rust-example-rendered{}",
271279
if ignore { " ignore" }
272280
else if compile_fail { " compile_fail" }
273-
else if edition.is_some() { " edition " }
281+
else if explicit_edition { " edition " }
274282
else { "" })),
275283
playground_button.as_ref().map(String::as_str),
276284
None));
@@ -659,7 +667,7 @@ impl LangString {
659667

660668
impl<'a> fmt::Display for Markdown<'a> {
661669
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
662-
let Markdown(md, links, ref ids, codes) = *self;
670+
let Markdown(md, links, ref ids, codes, edition) = *self;
663671
let mut ids = ids.borrow_mut();
664672

665673
// This is actually common enough to special-case
@@ -678,7 +686,7 @@ impl<'a> fmt::Display for Markdown<'a> {
678686

679687
let p = HeadingLinks::new(p, None, &mut ids);
680688
let p = LinkReplacer::new(p, links);
681-
let p = CodeBlocks::new(p, codes);
689+
let p = CodeBlocks::new(p, codes, edition);
682690
let p = Footnotes::new(p);
683691
html::push_html(&mut s, p);
684692

@@ -688,7 +696,7 @@ impl<'a> fmt::Display for Markdown<'a> {
688696

689697
impl<'a> fmt::Display for MarkdownWithToc<'a> {
690698
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
691-
let MarkdownWithToc(md, ref ids, codes) = *self;
699+
let MarkdownWithToc(md, ref ids, codes, edition) = *self;
692700
let mut ids = ids.borrow_mut();
693701

694702
let p = Parser::new_ext(md, opts());
@@ -699,7 +707,7 @@ impl<'a> fmt::Display for MarkdownWithToc<'a> {
699707

700708
{
701709
let p = HeadingLinks::new(p, Some(&mut toc), &mut ids);
702-
let p = CodeBlocks::new(p, codes);
710+
let p = CodeBlocks::new(p, codes, edition);
703711
let p = Footnotes::new(p);
704712
html::push_html(&mut s, p);
705713
}
@@ -712,7 +720,7 @@ impl<'a> fmt::Display for MarkdownWithToc<'a> {
712720

713721
impl<'a> fmt::Display for MarkdownHtml<'a> {
714722
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
715-
let MarkdownHtml(md, ref ids, codes) = *self;
723+
let MarkdownHtml(md, ref ids, codes, edition) = *self;
716724
let mut ids = ids.borrow_mut();
717725

718726
// This is actually common enough to special-case
@@ -728,7 +736,7 @@ impl<'a> fmt::Display for MarkdownHtml<'a> {
728736
let mut s = String::with_capacity(md.len() * 3 / 2);
729737

730738
let p = HeadingLinks::new(p, None, &mut ids);
731-
let p = CodeBlocks::new(p, codes);
739+
let p = CodeBlocks::new(p, codes, edition);
732740
let p = Footnotes::new(p);
733741
html::push_html(&mut s, p);
734742

@@ -1046,7 +1054,7 @@ mod tests {
10461054
use super::{ErrorCodes, LangString, Markdown, MarkdownHtml, IdMap};
10471055
use super::plain_summary_line;
10481056
use std::cell::RefCell;
1049-
use syntax::edition::Edition;
1057+
use syntax::edition::{Edition, DEFAULT_EDITION};
10501058

10511059
#[test]
10521060
fn test_lang_string_parse() {
@@ -1098,7 +1106,8 @@ mod tests {
10981106
fn test_header() {
10991107
fn t(input: &str, expect: &str) {
11001108
let mut map = IdMap::new();
1101-
let output = Markdown(input, &[], RefCell::new(&mut map), ErrorCodes::Yes).to_string();
1109+
let output = Markdown(input, &[], RefCell::new(&mut map),
1110+
ErrorCodes::Yes, DEFAULT_EDITION).to_string();
11021111
assert_eq!(output, expect, "original: {}", input);
11031112
}
11041113

@@ -1120,7 +1129,8 @@ mod tests {
11201129
fn test_header_ids_multiple_blocks() {
11211130
let mut map = IdMap::new();
11221131
fn t(map: &mut IdMap, input: &str, expect: &str) {
1123-
let output = Markdown(input, &[], RefCell::new(map), ErrorCodes::Yes).to_string();
1132+
let output = Markdown(input, &[], RefCell::new(map),
1133+
ErrorCodes::Yes, DEFAULT_EDITION).to_string();
11241134
assert_eq!(output, expect, "original: {}", input);
11251135
}
11261136

@@ -1157,7 +1167,8 @@ mod tests {
11571167
fn test_markdown_html_escape() {
11581168
fn t(input: &str, expect: &str) {
11591169
let mut idmap = IdMap::new();
1160-
let output = MarkdownHtml(input, RefCell::new(&mut idmap), ErrorCodes::Yes).to_string();
1170+
let output = MarkdownHtml(input, RefCell::new(&mut idmap),
1171+
ErrorCodes::Yes, DEFAULT_EDITION).to_string();
11611172
assert_eq!(output, expect, "original: {}", input);
11621173
}
11631174

src/librustdoc/html/render.rs

+12-6
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ use std::rc::Rc;
4747
use errors;
4848
use serialize::json::{ToJson, Json, as_json};
4949
use syntax::ast;
50+
use syntax::edition::Edition;
5051
use syntax::ext::base::MacroKind;
5152
use syntax::source_map::FileName;
5253
use syntax::feature_gate::UnstableFeatures;
@@ -108,6 +109,8 @@ struct Context {
108109
/// publicly reused items to redirect to the right location.
109110
pub render_redirect_pages: bool,
110111
pub codes: ErrorCodes,
112+
/// The default edition used to parse doctests.
113+
pub edition: Edition,
111114
/// The map used to ensure all generated 'id=' attributes are unique.
112115
id_map: Rc<RefCell<IdMap>>,
113116
pub shared: Arc<SharedContext>,
@@ -514,7 +517,8 @@ pub fn run(mut krate: clean::Crate,
514517
options: RenderOptions,
515518
passes: FxHashSet<String>,
516519
renderinfo: RenderInfo,
517-
diag: &errors::Handler) -> Result<(), Error> {
520+
diag: &errors::Handler,
521+
edition: Edition) -> Result<(), Error> {
518522
// need to save a copy of the options for rendering the index page
519523
let md_opts = options.clone();
520524
let RenderOptions {
@@ -604,6 +608,7 @@ pub fn run(mut krate: clean::Crate,
604608
dst,
605609
render_redirect_pages: false,
606610
codes: ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build()),
611+
edition,
607612
id_map: Rc::new(RefCell::new(id_map)),
608613
shared: Arc::new(scx),
609614
};
@@ -1128,7 +1133,7 @@ themePicker.onblur = handleThemeButtonsBlur;
11281133
md_opts.output = cx.dst.clone();
11291134
md_opts.external_html = (*cx.shared).layout.external_html.clone();
11301135

1131-
crate::markdown::render(index_page, md_opts, diag);
1136+
crate::markdown::render(index_page, md_opts, diag, cx.edition);
11321137
} else {
11331138
let dst = cx.dst.join("index.html");
11341139
let mut w = BufWriter::new(try_err!(File::create(&dst), &dst));
@@ -2553,7 +2558,7 @@ fn render_markdown(w: &mut fmt::Formatter<'_>,
25532558
if is_hidden { " hidden" } else { "" },
25542559
prefix,
25552560
Markdown(md_text, &links, RefCell::new(&mut ids),
2556-
cx.codes))
2561+
cx.codes, cx.edition))
25572562
}
25582563

25592564
fn document_short(
@@ -2918,7 +2923,7 @@ fn short_stability(item: &clean::Item, cx: &Context) -> Vec<String> {
29182923

29192924
if let Some(note) = note {
29202925
let mut ids = cx.id_map.borrow_mut();
2921-
let html = MarkdownHtml(&note, RefCell::new(&mut ids), error_codes);
2926+
let html = MarkdownHtml(&note, RefCell::new(&mut ids), error_codes, cx.edition);
29222927
message.push_str(&format!(": {}", html));
29232928
}
29242929
stability.push(format!("<div class='stab deprecated'>{}</div>", message));
@@ -2967,7 +2972,7 @@ fn short_stability(item: &clean::Item, cx: &Context) -> Vec<String> {
29672972
message = format!(
29682973
"<details><summary>{}</summary>{}</details>",
29692974
message,
2970-
MarkdownHtml(&unstable_reason, RefCell::new(&mut ids), error_codes)
2975+
MarkdownHtml(&unstable_reason, RefCell::new(&mut ids), error_codes, cx.edition)
29712976
);
29722977
}
29732978

@@ -4197,7 +4202,8 @@ fn render_impl(w: &mut fmt::Formatter<'_>, cx: &Context, i: &Impl, link: AssocIt
41974202
if let Some(ref dox) = cx.shared.maybe_collapsed_doc_value(&i.impl_item) {
41984203
let mut ids = cx.id_map.borrow_mut();
41994204
write!(w, "<div class='docblock'>{}</div>",
4200-
Markdown(&*dox, &i.impl_item.links(), RefCell::new(&mut ids), cx.codes))?;
4205+
Markdown(&*dox, &i.impl_item.links(), RefCell::new(&mut ids),
4206+
cx.codes, cx.edition))?;
42014207
}
42024208
}
42034209

0 commit comments

Comments
 (0)