diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2015-04-06 18:39:39 -0700 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2015-04-07 17:54:34 -0700 |
| commit | 179719d45023e549a62ec7a584d554408c6d241d (patch) | |
| tree | d63e2ae397eecbdd30f86936c8ca0bb6f9eb86e2 | |
| parent | 641bca06c82e2fa744e7b14bc45cfa501baf57e6 (diff) | |
| download | rust-179719d45023e549a62ec7a584d554408c6d241d.tar.gz rust-179719d45023e549a62ec7a584d554408c6d241d.zip | |
rustdoc: Allowing specifying attrs for doctests
This adds support in rustdoc to blanket apply crate attributes to all doc tests
for a crate at once. The syntax for doing this is:
#![doc(test(attr(...)))]
Each meta item in `...` will be applied to each doctest as a crate attribute.
cc #18199
| -rw-r--r-- | src/librustdoc/clean/mod.rs | 3 | ||||
| -rw-r--r-- | src/librustdoc/html/markdown.rs | 6 | ||||
| -rw-r--r-- | src/librustdoc/markdown.rs | 10 | ||||
| -rw-r--r-- | src/librustdoc/test.rs | 84 | ||||
| -rw-r--r-- | src/test/rustdoc/issue-18199.rs | 19 |
5 files changed, 78 insertions, 44 deletions
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 3248bb4d41d..002c6d7460b 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -199,7 +199,8 @@ impl<'a, 'tcx> Clean<Crate> for visit_ast::RustdocVisitor<'a, 'tcx> { module: Some(module), externs: externs, primitives: primitives, - external_traits: cx.external_traits.borrow_mut().take().unwrap(), + external_traits: cx.external_traits.borrow_mut().take() + .unwrap_or(HashMap::new()), } } } diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 49f6107869e..215f83ff87e 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -29,9 +29,10 @@ use libc; use std::ascii::AsciiExt; -use std::ffi::CString; use std::cell::RefCell; use std::collections::HashMap; +use std::default::Default; +use std::ffi::CString; use std::fmt; use std::slice; use std::str; @@ -244,7 +245,8 @@ pub fn render(w: &mut fmt::Formatter, s: &str, print_toc: bool) -> fmt::Result { stripped_filtered_line(l).unwrap_or(l) }).collect::<Vec<&str>>().connect("\n"); let krate = krate.as_ref().map(|s| &**s); - let test = test::maketest(&test, krate, false, false, true); + let test = test::maketest(&test, krate, false, + &Default::default()); s.push_str(&format!("<span class='rusttest'>{}</span>", Escape(&test))); }); s.push_str(&highlight::highlight(&text, diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index a84da60b018..8c75364d941 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -8,9 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::default::Default; use std::fs::File; -use std::io; use std::io::prelude::*; +use std::io; use std::path::{PathBuf, Path}; use core; @@ -23,7 +24,7 @@ use externalfiles::ExternalHtml; use html::escape::Escape; use html::markdown; use html::markdown::{Markdown, MarkdownWithToc, find_testable_code, reset_headers}; -use test::Collector; +use test::{TestOptions, Collector}; /// Separate any lines at the start of the file that begin with `%`. fn extract_leading_metadata<'a>(s: &'a str) -> (Vec<&'a str>, &'a str) { @@ -143,7 +144,10 @@ pub fn test(input: &str, libs: SearchPaths, externs: core::Externs, mut test_args: Vec<String>) -> isize { let input_str = load_or_return!(input, 1, 2); - let mut collector = Collector::new(input.to_string(), libs, externs, true, false); + let mut opts = TestOptions::default(); + opts.no_crate_inject = true; + let mut collector = Collector::new(input.to_string(), libs, externs, + true, opts); find_testable_code(&input_str, &mut collector); test_args.insert(0, "rustdoctest".to_string()); testing::test_main(&test_args, collector.tests); diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index f5bee6240d4..ffb2bb12540 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -38,6 +38,12 @@ use html::markdown; use passes; use visit_ast::RustdocVisitor; +#[derive(Clone, Default)] +pub struct TestOptions { + pub no_crate_inject: bool, + pub attrs: Vec<String>, +} + pub fn run(input: &str, cfgs: Vec<String>, libs: SearchPaths, @@ -75,7 +81,7 @@ pub fn run(input: &str, "rustdoc-test", None) .expect("phase_2_configure_and_expand aborted in rustdoc!"); - let inject_crate = should_inject_crate(&krate); + let opts = scrape_test_config(&krate); let ctx = core::DocContext { krate: &krate, @@ -102,7 +108,7 @@ pub fn run(input: &str, libs, externs, false, - inject_crate); + opts); collector.fold_crate(krate); test_args.insert(0, "rustdoctest".to_string()); @@ -113,41 +119,44 @@ pub fn run(input: &str, } // Look for #![doc(test(no_crate_inject))], used by crates in the std facade -fn should_inject_crate(krate: &::syntax::ast::Crate) -> bool { +fn scrape_test_config(krate: &::syntax::ast::Crate) -> TestOptions { use syntax::attr::AttrMetaMethods; + use syntax::print::pprust; - let mut inject_crate = true; - - for attr in &krate.attrs { - if attr.check_name("doc") { - for list in attr.meta_item_list().into_iter() { - for attr in list { - if attr.check_name("test") { - for list in attr.meta_item_list().into_iter() { - for attr in list { - if attr.check_name("no_crate_inject") { - inject_crate = false; - } - } - } - } + let mut opts = TestOptions { + no_crate_inject: true, + attrs: Vec::new(), + }; + + let attrs = krate.attrs.iter().filter(|a| a.check_name("doc")) + .filter_map(|a| a.meta_item_list()) + .flat_map(|l| l.iter()) + .filter(|a| a.check_name("test")) + .filter_map(|a| a.meta_item_list()) + .flat_map(|l| l.iter()); + for attr in attrs { + if attr.check_name("no_crate_inject") { + opts.no_crate_inject = true; + } + if attr.check_name("attr") { + if let Some(l) = attr.meta_item_list() { + for item in l { + opts.attrs.push(pprust::meta_item_to_string(item)); } } } } - return inject_crate; + return opts; } -#[allow(deprecated)] fn runtest(test: &str, cratename: &str, libs: SearchPaths, externs: core::Externs, should_panic: bool, no_run: bool, as_test_harness: bool, - inject_crate: bool) { + opts: &TestOptions) { // the test harness wants its own `main` & top level functions, so // never wrap the test in `fn main() { ... }` - let test = maketest(test, Some(cratename), true, as_test_harness, - inject_crate); + let test = maketest(test, Some(cratename), as_test_harness, opts); let input = config::Input::Str(test.to_string()); let sessopts = config::Options { @@ -250,8 +259,8 @@ fn runtest(test: &str, cratename: &str, libs: SearchPaths, } } -pub fn maketest(s: &str, cratename: Option<&str>, lints: bool, - dont_insert_main: bool, inject_crate: bool) -> String { +pub fn maketest(s: &str, cratename: Option<&str>, dont_insert_main: bool, + opts: &TestOptions) -> String { let (crate_attrs, everything_else) = partition_source(s); let mut prog = String::new(); @@ -260,20 +269,18 @@ pub fn maketest(s: &str, cratename: Option<&str>, lints: bool, // 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)] -"); + // Next, any attributes for other aspects such as lints. + for attr in &opts.attrs { + prog.push_str(&format!("#![{}]\n", attr)); } // Don't inject `extern crate std` because it's already injected by the // compiler. - if !s.contains("extern crate") && inject_crate { + if !s.contains("extern crate") && !opts.no_crate_inject { match cratename { Some(cratename) => { if s.contains(cratename) { - prog.push_str(&format!("extern crate {};\n", - cratename)); + prog.push_str(&format!("extern crate {};\n", cratename)); } } None => {} @@ -325,12 +332,12 @@ pub struct Collector { use_headers: bool, current_header: Option<String>, cratename: String, - inject_crate: bool + opts: TestOptions, } impl Collector { pub fn new(cratename: String, libs: SearchPaths, externs: core::Externs, - use_headers: bool, inject_crate: bool) -> Collector { + use_headers: bool, opts: TestOptions) -> Collector { Collector { tests: Vec::new(), names: Vec::new(), @@ -340,7 +347,7 @@ impl Collector { use_headers: use_headers, current_header: None, cratename: cratename, - inject_crate: inject_crate + opts: opts, } } @@ -357,13 +364,14 @@ impl Collector { let libs = self.libs.clone(); let externs = self.externs.clone(); let cratename = self.cratename.to_string(); - let inject_crate = self.inject_crate; + let opts = self.opts.clone(); debug!("Creating test {}: {}", name, test); self.tests.push(testing::TestDescAndFn { desc: testing::TestDesc { name: testing::DynTestName(name), ignore: should_ignore, - should_panic: testing::ShouldPanic::No, // compiler failures are test failures + // compiler failures are test failures + should_panic: testing::ShouldPanic::No, }, testfn: testing::DynTestFn(Box::new(move|| { runtest(&test, @@ -373,7 +381,7 @@ impl Collector { should_panic, no_run, as_test_harness, - inject_crate); + &opts); })) }); } diff --git a/src/test/rustdoc/issue-18199.rs b/src/test/rustdoc/issue-18199.rs new file mode 100644 index 00000000000..46aac8701fd --- /dev/null +++ b/src/test/rustdoc/issue-18199.rs @@ -0,0 +1,19 @@ +// Copyright 2015 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 + +#![doc(test(attr(feature(staged_api))))] + +/// ``` +/// #![staged_api] +/// fn main() {} +/// ``` +pub fn foo() {} |
