diff --git a/src/doc/rustdoc/src/documentation-tests.md b/src/doc/rustdoc/src/documentation-tests.md index e4af122d0cb98..dd8dcb7ff9bd2 100644 --- a/src/doc/rustdoc/src/documentation-tests.md +++ b/src/doc/rustdoc/src/documentation-tests.md @@ -323,6 +323,22 @@ compiles, then the test will fail. However please note that code failing with the current Rust release may work in a future release, as new features are added. +```text +/// Only runs on the 2018 edition. +/// +/// ```edition2018 +/// let result: Result = try { +/// "1".parse::()? +/// + "2".parse::()? +/// + "3".parse::()? +/// }; +/// ``` +``` + +`edition2018` tells `rustdoc` that the code sample should be compiled the 2018 +edition of Rust. Similarly, you can specify `edition2015` to compile the code +with the 2015 edition. + ## Syntax reference The *exact* syntax for code blocks, including the edge cases, can be found diff --git a/src/doc/unstable-book/src/language-features/try-blocks.md b/src/doc/unstable-book/src/language-features/try-blocks.md index 866b37a39a781..e342c260a739b 100644 --- a/src/doc/unstable-book/src/language-features/try-blocks.md +++ b/src/doc/unstable-book/src/language-features/try-blocks.md @@ -9,9 +9,7 @@ The tracking issue for this feature is: [#31436] The `try_blocks` feature adds support for `try` blocks. A `try` block creates a new scope one can use the `?` operator in. -```rust,ignore -// This code needs the 2018 edition - +```rust,edition2018 #![feature(try_blocks)] use std::num::ParseIntError; diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 18ad862c11bb0..d14275aeb6bf5 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -37,6 +37,7 @@ use std::fmt::{self, Write}; use std::borrow::Cow; use std::ops::Range; use std::str; +use syntax::edition::Edition; use html::toc::TocBuilder; use html::highlight; @@ -170,6 +171,7 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'a, I> { let event = self.inner.next(); let compile_fail; let ignore; + let edition; if let Some(Event::Start(Tag::CodeBlock(lang))) = event { let parse_result = LangString::parse(&lang, self.check_error_codes); if !parse_result.rust { @@ -177,6 +179,7 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'a, I> { } compile_fail = parse_result.compile_fail; ignore = parse_result.ignore; + edition = parse_result.edition; } else { return event; } @@ -212,6 +215,17 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'a, I> { } else { "" }; + + let edition_string = if let Some(e @ Edition::Edition2018) = edition { + format!("&edition={}{}", e, + if channel == "&version=nightly" { "" } + else { "&version=nightly" }) + } else if let Some(e) = edition { + format!("&edition={}", e) + } else { + "".to_owned() + }; + // These characters don't need to be escaped in a URI. // FIXME: use a library function for percent encoding. fn dont_escape(c: u8) -> bool { @@ -231,26 +245,44 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'a, I> { } } Some(format!( - r#"Run"#, - url, test_escaped, channel + r#"Run"#, + url, test_escaped, channel, edition_string )) }); + let tooltip = if ignore { - Some(("This example is not tested", "ignore")) + Some(("This example is not tested".to_owned(), "ignore")) } else if compile_fail { - Some(("This example deliberately fails to compile", "compile_fail")) + Some(("This example deliberately fails to compile".to_owned(), "compile_fail")) + } else if let Some(e) = edition { + Some((format!("This code runs with edition {}", e), "edition")) } else { None }; - s.push_str(&highlight::render_with_highlighting( - &text, - Some(&format!("rust-example-rendered{}", - if ignore { " ignore" } - else if compile_fail { " compile_fail" } - else { "" })), - playground_button.as_ref().map(String::as_str), - tooltip)); - Some(Event::Html(s.into())) + + if let Some((s1, s2)) = tooltip { + s.push_str(&highlight::render_with_highlighting( + &text, + Some(&format!("rust-example-rendered{}", + if ignore { " ignore" } + else if compile_fail { " compile_fail" } + else if edition.is_some() { " edition " } + else { "" })), + playground_button.as_ref().map(String::as_str), + Some((s1.as_str(), s2)))); + Some(Event::Html(s.into())) + } else { + s.push_str(&highlight::render_with_highlighting( + &text, + Some(&format!("rust-example-rendered{}", + if ignore { " ignore" } + else if compile_fail { " compile_fail" } + else if edition.is_some() { " edition " } + else { "" })), + playground_button.as_ref().map(String::as_str), + None)); + Some(Event::Html(s.into())) + } }) } } @@ -577,6 +609,7 @@ pub struct LangString { pub compile_fail: bool, pub error_codes: Vec, pub allow_fail: bool, + pub edition: Option } impl LangString { @@ -591,6 +624,7 @@ impl LangString { compile_fail: false, error_codes: Vec::new(), allow_fail: false, + edition: None, } } @@ -625,6 +659,11 @@ impl LangString { seen_rust_tags = !seen_other_tags || seen_rust_tags; data.no_run = true; } + x if allow_error_code_check && x.starts_with("edition") => { + // allow_error_code_check is true if we're on nightly, which + // is needed for edition support + data.edition = x[7..].parse::().ok(); + } x if allow_error_code_check && x.starts_with("E") && x.len() == 5 => { if x[1..].parse::().is_ok() { data.error_codes.push(x.to_owned()); @@ -925,12 +964,14 @@ mod tests { use super::{ErrorCodes, LangString, Markdown, MarkdownHtml, IdMap}; use super::plain_summary_line; use std::cell::RefCell; + use syntax::edition::Edition; #[test] fn test_lang_string_parse() { fn t(s: &str, should_panic: bool, no_run: bool, ignore: bool, rust: bool, test_harness: bool, - compile_fail: bool, allow_fail: bool, error_codes: Vec) { + compile_fail: bool, allow_fail: bool, error_codes: Vec, + edition: Option) { assert_eq!(LangString::parse(s, ErrorCodes::Yes), LangString { should_panic, no_run, @@ -941,6 +982,7 @@ mod tests { error_codes, original: s.to_owned(), allow_fail, + edition, }) } @@ -948,23 +990,26 @@ mod tests { Vec::new() } - // marker | should_panic| no_run| ignore| rust | test_harness| compile_fail - // | allow_fail | error_codes - t("", false, false, false, true, false, false, false, v()); - t("rust", false, false, false, true, false, false, false, v()); - t("sh", false, false, false, false, false, false, false, v()); - t("ignore", false, false, true, true, false, false, false, v()); - t("should_panic", true, false, false, true, false, false, false, v()); - t("no_run", false, true, false, true, false, false, false, v()); - t("test_harness", false, false, false, true, true, false, false, v()); - t("compile_fail", false, true, false, true, false, true, false, v()); - t("allow_fail", false, false, false, true, false, false, true, v()); - t("{.no_run .example}", false, true, false, true, false, false, false, v()); - t("{.sh .should_panic}", true, false, false, false, false, false, false, v()); - t("{.example .rust}", false, false, false, true, false, false, false, v()); - t("{.test_harness .rust}", false, false, false, true, true, false, false, v()); - t("text, no_run", false, true, false, false, false, false, false, v()); - t("text,no_run", false, true, false, false, false, false, false, v()); + // ignore-tidy-linelength + // marker | should_panic | no_run | ignore | rust | test_harness + // | compile_fail | allow_fail | error_codes | edition + t("", false, false, false, true, false, false, false, v(), None); + t("rust", false, false, false, true, false, false, false, v(), None); + t("sh", false, false, false, false, false, false, false, v(), None); + t("ignore", false, false, true, true, false, false, false, v(), None); + t("should_panic", true, false, false, true, false, false, false, v(), None); + t("no_run", false, true, false, true, false, false, false, v(), None); + t("test_harness", false, false, false, true, true, false, false, v(), None); + t("compile_fail", false, true, false, true, false, true, false, v(), None); + t("allow_fail", false, false, false, true, false, false, true, v(), None); + t("{.no_run .example}", false, true, false, true, false, false, false, v(), None); + t("{.sh .should_panic}", true, false, false, false, false, false, false, v(), None); + t("{.example .rust}", false, false, false, true, false, false, false, v(), None); + t("{.test_harness .rust}", false, false, false, true, true, false, false, v(), None); + t("text, no_run", false, true, false, false, false, false, false, v(), None); + t("text,no_run", false, true, false, false, false, false, false, v(), None); + t("edition2015", false, false, false, true, false, false, false, v(), Some(Edition::Edition2015)); + t("edition2018", false, false, false, true, false, false, false, v(), Some(Edition::Edition2018)); } #[test] diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 3b07a2ccdde09..c33888d1a49e5 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -545,7 +545,7 @@ impl Collector { let opts = self.opts.clone(); let maybe_sysroot = self.maybe_sysroot.clone(); let linker = self.linker.clone(); - let edition = self.edition; + let edition = config.edition.unwrap_or(self.edition); debug!("Creating test {}: {}", name, test); self.tests.push(testing::TestDescAndFn { desc: testing::TestDesc { diff --git a/src/libsyntax_pos/edition.rs b/src/libsyntax_pos/edition.rs index 7709db72a02fb..5819cd7f480a0 100644 --- a/src/libsyntax_pos/edition.rs +++ b/src/libsyntax_pos/edition.rs @@ -12,7 +12,7 @@ use std::fmt; use std::str::FromStr; /// The edition of the compiler (RFC 2052) -#[derive(Clone, Copy, Hash, PartialEq, PartialOrd, Debug, RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, Hash, PartialEq, PartialOrd, Debug, RustcEncodable, RustcDecodable, Eq)] #[non_exhaustive] pub enum Edition { // editions must be kept in order, oldest to newest diff --git a/src/test/rustdoc/edition-doctest.rs b/src/test/rustdoc/edition-doctest.rs new file mode 100644 index 0000000000000..322d461f854e9 --- /dev/null +++ b/src/test/rustdoc/edition-doctest.rs @@ -0,0 +1,54 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags:--test + +/// ```rust,edition2018 +/// #![feature(try_blocks)] +/// +/// use std::num::ParseIntError; +/// +/// let result: Result = try { +/// "1".parse::()? +/// + "2".parse::()? +/// + "3".parse::()? +/// }; +/// assert_eq!(result, Ok(6)); +/// +/// let result: Result = try { +/// "1".parse::()? +/// + "foo".parse::()? +/// + "3".parse::()? +/// }; +/// assert!(result.is_err()); +/// ``` + + +/// ```rust,edition2015,compile_fail,E0574 +/// #![feature(try_blocks)] +/// +/// use std::num::ParseIntError; +/// +/// let result: Result = try { +/// "1".parse::()? +/// + "2".parse::()? +/// + "3".parse::()? +/// }; +/// assert_eq!(result, Ok(6)); +/// +/// let result: Result = try { +/// "1".parse::()? +/// + "foo".parse::()? +/// + "3".parse::()? +/// }; +/// assert!(result.is_err()); +/// ``` + +pub fn foo() {}