rustdoc: interpret all leading feature attributes in examples as crate attributes

This makes it possible to write `#![feature(foo)]` in doc tests.
This commit is contained in:
Brian Anderson 2015-03-12 13:01:06 -07:00
parent 809a554fca
commit 3d365f6a01
3 changed files with 46 additions and 9 deletions

View File

@ -237,14 +237,17 @@ fn main() {
}
```
Here's the full algorithm:
Here's the full algorithm rustdoc uses to postprocess examples:
1. Given a code block, if it does not contain `fn main()`, it is wrapped in
`fn main() { your_code }`
2. Given that result, if it contains no `extern crate` directives but it also
contains the name of the crate being tested, then `extern crate <name>` is
injected at the top.
3. Some common allow attributes are added for documentation examples at the top.
1. Any leading `#![foo]` attributes are left intact as crate attributes.
2. Some common `allow` attributes are inserted, including
`unused_variables`, `unused_assignments`, `unused_mut`,
`unused_attributes`, and `dead_code`. Small examples often trigger
these lints.
3. If the example does not contain `extern crate`, then `extern crate
<mycrate>;` is inserted.
2. Finally, if the example does not contain `fn main`, the remainder of the
text is wrapped in `fn main() { your_code }`
Sometimes, this isn't enough, though. For example, all of these code samples
with `///` we've been talking about? The raw text:

View File

@ -51,6 +51,7 @@ extern crate rustc_back;
extern crate serialize;
extern crate syntax;
extern crate "test" as testing;
extern crate unicode;
#[macro_use] extern crate log;
extern crate "serialize" as rustc_serialize; // used by deriving

View File

@ -219,7 +219,14 @@ fn runtest(test: &str, cratename: &str, libs: SearchPaths,
}
pub fn maketest(s: &str, cratename: Option<&str>, lints: bool, dont_insert_main: bool) -> String {
let (crate_attrs, everything_else) = partition_source(s);
let mut prog = String::new();
// First push any outer attributes from the example, assuming they
// are intended to be crate attributes.
prog.push_str(&crate_attrs);
if lints {
prog.push_str(r"
#![allow(unused_variables, unused_assignments, unused_mut, unused_attributes, dead_code)]
@ -240,16 +247,42 @@ pub fn maketest(s: &str, cratename: Option<&str>, lints: bool, dont_insert_main:
}
}
if dont_insert_main || s.contains("fn main") {
prog.push_str(s);
prog.push_str(&everything_else);
} else {
prog.push_str("fn main() {\n ");
prog.push_str(&s.replace("\n", "\n "));
prog.push_str(&everything_else.replace("\n", "\n "));
prog.push_str("\n}");
}
info!("final test program: {}", prog);
return prog
}
fn partition_source(s: &str) -> (String, String) {
use unicode::str::UnicodeStr;
let mut after_header = false;
let mut before = String::new();
let mut after = String::new();
for line in s.lines() {
let trimline = StrExt::trim(line);
let header = trimline.is_whitespace() ||
trimline.starts_with("#![feature");
if !header || after_header {
after_header = true;
after.push_str(line);
after.push_str("\n");
} else {
before.push_str(line);
before.push_str("\n");
}
}
return (before, after);
}
pub struct Collector {
pub tests: Vec<testing::TestDescAndFn>,
names: Vec<String>,