Skip to content

Commit 23636e3

Browse files
authored
Rollup merge of rust-lang#54350 - Munksgaard:support-edition-in-doc-test, r=steveklabnik
Support specifying edition in doc test Fixes rust-lang#52623 r? @QuietMisdreavus
2 parents e59df62 + 06b1975 commit 23636e3

File tree

6 files changed

+149
-36
lines changed

6 files changed

+149
-36
lines changed

src/doc/rustdoc/src/documentation-tests.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,22 @@ compiles, then the test will fail. However please note that code failing
323323
with the current Rust release may work in a future release, as new features
324324
are added.
325325

326+
```text
327+
/// Only runs on the 2018 edition.
328+
///
329+
/// ```edition2018
330+
/// let result: Result<i32, ParseIntError> = try {
331+
/// "1".parse::<i32>()?
332+
/// + "2".parse::<i32>()?
333+
/// + "3".parse::<i32>()?
334+
/// };
335+
/// ```
336+
```
337+
338+
`edition2018` tells `rustdoc` that the code sample should be compiled the 2018
339+
edition of Rust. Similarly, you can specify `edition2015` to compile the code
340+
with the 2015 edition.
341+
326342
## Syntax reference
327343

328344
The *exact* syntax for code blocks, including the edge cases, can be found

src/doc/unstable-book/src/language-features/try-blocks.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@ The tracking issue for this feature is: [#31436]
99
The `try_blocks` feature adds support for `try` blocks. A `try`
1010
block creates a new scope one can use the `?` operator in.
1111

12-
```rust,ignore
13-
// This code needs the 2018 edition
14-
12+
```rust,edition2018
1513
#![feature(try_blocks)]
1614
1715
use std::num::ParseIntError;

src/librustdoc/html/markdown.rs

Lines changed: 76 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ use std::fmt::{self, Write};
3737
use std::borrow::Cow;
3838
use std::ops::Range;
3939
use std::str;
40+
use syntax::edition::Edition;
4041

4142
use html::toc::TocBuilder;
4243
use html::highlight;
@@ -170,13 +171,15 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'a, I> {
170171
let event = self.inner.next();
171172
let compile_fail;
172173
let ignore;
174+
let edition;
173175
if let Some(Event::Start(Tag::CodeBlock(lang))) = event {
174176
let parse_result = LangString::parse(&lang, self.check_error_codes);
175177
if !parse_result.rust {
176178
return Some(Event::Start(Tag::CodeBlock(lang)));
177179
}
178180
compile_fail = parse_result.compile_fail;
179181
ignore = parse_result.ignore;
182+
edition = parse_result.edition;
180183
} else {
181184
return event;
182185
}
@@ -212,6 +215,17 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'a, I> {
212215
} else {
213216
""
214217
};
218+
219+
let edition_string = if let Some(e @ Edition::Edition2018) = edition {
220+
format!("&amp;edition={}{}", e,
221+
if channel == "&amp;version=nightly" { "" }
222+
else { "&amp;version=nightly" })
223+
} else if let Some(e) = edition {
224+
format!("&amp;edition={}", e)
225+
} else {
226+
"".to_owned()
227+
};
228+
215229
// These characters don't need to be escaped in a URI.
216230
// FIXME: use a library function for percent encoding.
217231
fn dont_escape(c: u8) -> bool {
@@ -231,26 +245,44 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'a, I> {
231245
}
232246
}
233247
Some(format!(
234-
r#"<a class="test-arrow" target="_blank" href="{}?code={}{}">Run</a>"#,
235-
url, test_escaped, channel
248+
r#"<a class="test-arrow" target="_blank" href="{}?code={}{}{}">Run</a>"#,
249+
url, test_escaped, channel, edition_string
236250
))
237251
});
252+
238253
let tooltip = if ignore {
239-
Some(("This example is not tested", "ignore"))
254+
Some(("This example is not tested".to_owned(), "ignore"))
240255
} else if compile_fail {
241-
Some(("This example deliberately fails to compile", "compile_fail"))
256+
Some(("This example deliberately fails to compile".to_owned(), "compile_fail"))
257+
} else if let Some(e) = edition {
258+
Some((format!("This code runs with edition {}", e), "edition"))
242259
} else {
243260
None
244261
};
245-
s.push_str(&highlight::render_with_highlighting(
246-
&text,
247-
Some(&format!("rust-example-rendered{}",
248-
if ignore { " ignore" }
249-
else if compile_fail { " compile_fail" }
250-
else { "" })),
251-
playground_button.as_ref().map(String::as_str),
252-
tooltip));
253-
Some(Event::Html(s.into()))
262+
263+
if let Some((s1, s2)) = tooltip {
264+
s.push_str(&highlight::render_with_highlighting(
265+
&text,
266+
Some(&format!("rust-example-rendered{}",
267+
if ignore { " ignore" }
268+
else if compile_fail { " compile_fail" }
269+
else if edition.is_some() { " edition " }
270+
else { "" })),
271+
playground_button.as_ref().map(String::as_str),
272+
Some((s1.as_str(), s2))));
273+
Some(Event::Html(s.into()))
274+
} else {
275+
s.push_str(&highlight::render_with_highlighting(
276+
&text,
277+
Some(&format!("rust-example-rendered{}",
278+
if ignore { " ignore" }
279+
else if compile_fail { " compile_fail" }
280+
else if edition.is_some() { " edition " }
281+
else { "" })),
282+
playground_button.as_ref().map(String::as_str),
283+
None));
284+
Some(Event::Html(s.into()))
285+
}
254286
})
255287
}
256288
}
@@ -577,6 +609,7 @@ pub struct LangString {
577609
pub compile_fail: bool,
578610
pub error_codes: Vec<String>,
579611
pub allow_fail: bool,
612+
pub edition: Option<Edition>
580613
}
581614

582615
impl LangString {
@@ -591,6 +624,7 @@ impl LangString {
591624
compile_fail: false,
592625
error_codes: Vec::new(),
593626
allow_fail: false,
627+
edition: None,
594628
}
595629
}
596630

@@ -625,6 +659,11 @@ impl LangString {
625659
seen_rust_tags = !seen_other_tags || seen_rust_tags;
626660
data.no_run = true;
627661
}
662+
x if allow_error_code_check && x.starts_with("edition") => {
663+
// allow_error_code_check is true if we're on nightly, which
664+
// is needed for edition support
665+
data.edition = x[7..].parse::<Edition>().ok();
666+
}
628667
x if allow_error_code_check && x.starts_with("E") && x.len() == 5 => {
629668
if x[1..].parse::<u32>().is_ok() {
630669
data.error_codes.push(x.to_owned());
@@ -925,12 +964,14 @@ mod tests {
925964
use super::{ErrorCodes, LangString, Markdown, MarkdownHtml, IdMap};
926965
use super::plain_summary_line;
927966
use std::cell::RefCell;
967+
use syntax::edition::Edition;
928968

929969
#[test]
930970
fn test_lang_string_parse() {
931971
fn t(s: &str,
932972
should_panic: bool, no_run: bool, ignore: bool, rust: bool, test_harness: bool,
933-
compile_fail: bool, allow_fail: bool, error_codes: Vec<String>) {
973+
compile_fail: bool, allow_fail: bool, error_codes: Vec<String>,
974+
edition: Option<Edition>) {
934975
assert_eq!(LangString::parse(s, ErrorCodes::Yes), LangString {
935976
should_panic,
936977
no_run,
@@ -941,30 +982,34 @@ mod tests {
941982
error_codes,
942983
original: s.to_owned(),
943984
allow_fail,
985+
edition,
944986
})
945987
}
946988

947989
fn v() -> Vec<String> {
948990
Vec::new()
949991
}
950992

951-
// marker | should_panic| no_run| ignore| rust | test_harness| compile_fail
952-
// | allow_fail | error_codes
953-
t("", false, false, false, true, false, false, false, v());
954-
t("rust", false, false, false, true, false, false, false, v());
955-
t("sh", false, false, false, false, false, false, false, v());
956-
t("ignore", false, false, true, true, false, false, false, v());
957-
t("should_panic", true, false, false, true, false, false, false, v());
958-
t("no_run", false, true, false, true, false, false, false, v());
959-
t("test_harness", false, false, false, true, true, false, false, v());
960-
t("compile_fail", false, true, false, true, false, true, false, v());
961-
t("allow_fail", false, false, false, true, false, false, true, v());
962-
t("{.no_run .example}", false, true, false, true, false, false, false, v());
963-
t("{.sh .should_panic}", true, false, false, false, false, false, false, v());
964-
t("{.example .rust}", false, false, false, true, false, false, false, v());
965-
t("{.test_harness .rust}", false, false, false, true, true, false, false, v());
966-
t("text, no_run", false, true, false, false, false, false, false, v());
967-
t("text,no_run", false, true, false, false, false, false, false, v());
993+
// ignore-tidy-linelength
994+
// marker | should_panic | no_run | ignore | rust | test_harness
995+
// | compile_fail | allow_fail | error_codes | edition
996+
t("", false, false, false, true, false, false, false, v(), None);
997+
t("rust", false, false, false, true, false, false, false, v(), None);
998+
t("sh", false, false, false, false, false, false, false, v(), None);
999+
t("ignore", false, false, true, true, false, false, false, v(), None);
1000+
t("should_panic", true, false, false, true, false, false, false, v(), None);
1001+
t("no_run", false, true, false, true, false, false, false, v(), None);
1002+
t("test_harness", false, false, false, true, true, false, false, v(), None);
1003+
t("compile_fail", false, true, false, true, false, true, false, v(), None);
1004+
t("allow_fail", false, false, false, true, false, false, true, v(), None);
1005+
t("{.no_run .example}", false, true, false, true, false, false, false, v(), None);
1006+
t("{.sh .should_panic}", true, false, false, false, false, false, false, v(), None);
1007+
t("{.example .rust}", false, false, false, true, false, false, false, v(), None);
1008+
t("{.test_harness .rust}", false, false, false, true, true, false, false, v(), None);
1009+
t("text, no_run", false, true, false, false, false, false, false, v(), None);
1010+
t("text,no_run", false, true, false, false, false, false, false, v(), None);
1011+
t("edition2015", false, false, false, true, false, false, false, v(), Some(Edition::Edition2015));
1012+
t("edition2018", false, false, false, true, false, false, false, v(), Some(Edition::Edition2018));
9681013
}
9691014

9701015
#[test]

src/librustdoc/test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -545,7 +545,7 @@ impl Collector {
545545
let opts = self.opts.clone();
546546
let maybe_sysroot = self.maybe_sysroot.clone();
547547
let linker = self.linker.clone();
548-
let edition = self.edition;
548+
let edition = config.edition.unwrap_or(self.edition);
549549
debug!("Creating test {}: {}", name, test);
550550
self.tests.push(testing::TestDescAndFn {
551551
desc: testing::TestDesc {

src/libsyntax_pos/edition.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use std::fmt;
1212
use std::str::FromStr;
1313

1414
/// The edition of the compiler (RFC 2052)
15-
#[derive(Clone, Copy, Hash, PartialEq, PartialOrd, Debug, RustcEncodable, RustcDecodable)]
15+
#[derive(Clone, Copy, Hash, PartialEq, PartialOrd, Debug, RustcEncodable, RustcDecodable, Eq)]
1616
#[non_exhaustive]
1717
pub enum Edition {
1818
// editions must be kept in order, oldest to newest

src/test/rustdoc/edition-doctest.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// compile-flags:--test
12+
13+
/// ```rust,edition2018
14+
/// #![feature(try_blocks)]
15+
///
16+
/// use std::num::ParseIntError;
17+
///
18+
/// let result: Result<i32, ParseIntError> = try {
19+
/// "1".parse::<i32>()?
20+
/// + "2".parse::<i32>()?
21+
/// + "3".parse::<i32>()?
22+
/// };
23+
/// assert_eq!(result, Ok(6));
24+
///
25+
/// let result: Result<i32, ParseIntError> = try {
26+
/// "1".parse::<i32>()?
27+
/// + "foo".parse::<i32>()?
28+
/// + "3".parse::<i32>()?
29+
/// };
30+
/// assert!(result.is_err());
31+
/// ```
32+
33+
34+
/// ```rust,edition2015,compile_fail,E0574
35+
/// #![feature(try_blocks)]
36+
///
37+
/// use std::num::ParseIntError;
38+
///
39+
/// let result: Result<i32, ParseIntError> = try {
40+
/// "1".parse::<i32>()?
41+
/// + "2".parse::<i32>()?
42+
/// + "3".parse::<i32>()?
43+
/// };
44+
/// assert_eq!(result, Ok(6));
45+
///
46+
/// let result: Result<i32, ParseIntError> = try {
47+
/// "1".parse::<i32>()?
48+
/// + "foo".parse::<i32>()?
49+
/// + "3".parse::<i32>()?
50+
/// };
51+
/// assert!(result.is_err());
52+
/// ```
53+
54+
pub fn foo() {}

0 commit comments

Comments
 (0)