Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
rustdoc: interpret all leading feature attributes in examples as crat…
…e attributes

This makes it possible to write `#![feature(foo)]` in doc tests.
  • Loading branch information
brson committed Mar 23, 2015
1 parent 809a554 commit 3d365f6
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 10 deletions.
19 changes: 11 additions & 8 deletions src/doc/trpl/documentation.md
Expand Up @@ -237,14 +237,17 @@ fn main() {
}
```
Here's the full algorithm:
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.
Here's the full algorithm rustdoc uses to postprocess examples:
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:
Expand Down
1 change: 1 addition & 0 deletions src/librustdoc/lib.rs
Expand Up @@ -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
Expand Down
37 changes: 35 additions & 2 deletions src/librustdoc/test.rs
Expand Up @@ -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)]
Expand All @@ -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>,
Expand Down

0 comments on commit 3d365f6

Please sign in to comment.