From 3d365f6a01c4ebd7847ef7ec64afdcc54129a055 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 12 Mar 2015 13:01:06 -0700 Subject: [PATCH] rustdoc: interpret all leading feature attributes in examples as crate attributes This makes it possible to write `#![feature(foo)]` in doc tests. --- src/doc/trpl/documentation.md | 17 +++++++++------- src/librustdoc/lib.rs | 1 + src/librustdoc/test.rs | 37 +++++++++++++++++++++++++++++++++-- 3 files changed, 46 insertions(+), 9 deletions(-) diff --git a/src/doc/trpl/documentation.md b/src/doc/trpl/documentation.md index 8e5b3b6a7f0..643554e6563 100644 --- a/src/doc/trpl/documentation.md +++ b/src/doc/trpl/documentation.md @@ -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 ` 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 + ;` 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: diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index d747ed3f119..aa0f60d0d1c 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -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 diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index e2f8a6f82c6..3bd466caf0b 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -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, names: Vec,