about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/doc/trpl/documentation.md19
-rw-r--r--src/librustdoc/lib.rs1
-rw-r--r--src/librustdoc/test.rs37
3 files changed, 47 insertions, 10 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:
-
-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:
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<testing::TestDescAndFn>,
     names: Vec<String>,