Skip to content

Commit

Permalink
Rollup merge of rust-lang#54350 - Munksgaard:support-edition-in-doc-t…
Browse files Browse the repository at this point in the history
…est, r=steveklabnik

Support specifying edition in doc test

Fixes rust-lang#52623

r? @QuietMisdreavus
  • Loading branch information
pietroalbini authored Sep 22, 2018
2 parents e59df62 + 06b1975 commit 23636e3
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 36 deletions.
16 changes: 16 additions & 0 deletions src/doc/rustdoc/src/documentation-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<i32, ParseIntError> = try {
/// "1".parse::<i32>()?
/// + "2".parse::<i32>()?
/// + "3".parse::<i32>()?
/// };
/// ```
```

`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
Expand Down
4 changes: 1 addition & 3 deletions src/doc/unstable-book/src/language-features/try-blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
107 changes: 76 additions & 31 deletions src/librustdoc/html/markdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -170,13 +171,15 @@ impl<'a, I: Iterator<Item = Event<'a>>> 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 {
return Some(Event::Start(Tag::CodeBlock(lang)));
}
compile_fail = parse_result.compile_fail;
ignore = parse_result.ignore;
edition = parse_result.edition;
} else {
return event;
}
Expand Down Expand Up @@ -212,6 +215,17 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'a, I> {
} else {
""
};

let edition_string = if let Some(e @ Edition::Edition2018) = edition {
format!("&amp;edition={}{}", e,
if channel == "&amp;version=nightly" { "" }
else { "&amp;version=nightly" })
} else if let Some(e) = edition {
format!("&amp;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 {
Expand All @@ -231,26 +245,44 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'a, I> {
}
}
Some(format!(
r#"<a class="test-arrow" target="_blank" href="{}?code={}{}">Run</a>"#,
url, test_escaped, channel
r#"<a class="test-arrow" target="_blank" href="{}?code={}{}{}">Run</a>"#,
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()))
}
})
}
}
Expand Down Expand Up @@ -577,6 +609,7 @@ pub struct LangString {
pub compile_fail: bool,
pub error_codes: Vec<String>,
pub allow_fail: bool,
pub edition: Option<Edition>
}

impl LangString {
Expand All @@ -591,6 +624,7 @@ impl LangString {
compile_fail: false,
error_codes: Vec::new(),
allow_fail: false,
edition: None,
}
}

Expand Down Expand Up @@ -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::<Edition>().ok();
}
x if allow_error_code_check && x.starts_with("E") && x.len() == 5 => {
if x[1..].parse::<u32>().is_ok() {
data.error_codes.push(x.to_owned());
Expand Down Expand Up @@ -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<String>) {
compile_fail: bool, allow_fail: bool, error_codes: Vec<String>,
edition: Option<Edition>) {
assert_eq!(LangString::parse(s, ErrorCodes::Yes), LangString {
should_panic,
no_run,
Expand All @@ -941,30 +982,34 @@ mod tests {
error_codes,
original: s.to_owned(),
allow_fail,
edition,
})
}

fn v() -> Vec<String> {
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]
Expand Down
2 changes: 1 addition & 1 deletion src/librustdoc/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion src/libsyntax_pos/edition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
54 changes: 54 additions & 0 deletions src/test/rustdoc/edition-doctest.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<i32, ParseIntError> = try {
/// "1".parse::<i32>()?
/// + "2".parse::<i32>()?
/// + "3".parse::<i32>()?
/// };
/// assert_eq!(result, Ok(6));
///
/// let result: Result<i32, ParseIntError> = try {
/// "1".parse::<i32>()?
/// + "foo".parse::<i32>()?
/// + "3".parse::<i32>()?
/// };
/// assert!(result.is_err());
/// ```

/// ```rust,edition2015,compile_fail,E0574
/// #![feature(try_blocks)]
///
/// use std::num::ParseIntError;
///
/// let result: Result<i32, ParseIntError> = try {
/// "1".parse::<i32>()?
/// + "2".parse::<i32>()?
/// + "3".parse::<i32>()?
/// };
/// assert_eq!(result, Ok(6));
///
/// let result: Result<i32, ParseIntError> = try {
/// "1".parse::<i32>()?
/// + "foo".parse::<i32>()?
/// + "3".parse::<i32>()?
/// };
/// assert!(result.is_err());
/// ```
pub fn foo() {}

0 comments on commit 23636e3

Please sign in to comment.