#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Debug, Default)]
pub struct Attributes {
pub doc_strings: Vec<String>,
- pub other_attrs: Vec<ast::Attribute>
+ pub other_attrs: Vec<ast::Attribute>,
+ pub span: Option<syntax_pos::Span>,
}
impl Attributes {
pub fn from_ast(attrs: &[ast::Attribute]) -> Attributes {
let mut doc_strings = vec![];
+ let mut sp = None;
let other_attrs = attrs.iter().filter_map(|attr| {
attr.with_desugared_doc(|attr| {
if let Some(value) = attr.value_str() {
if attr.check_name("doc") {
doc_strings.push(value.to_string());
+ if sp.is_none() {
+ sp = Some(attr.span);
+ }
return None;
}
}
}).collect();
Attributes {
doc_strings: doc_strings,
- other_attrs: other_attrs
+ other_attrs: other_attrs,
+ span: sp,
}
}
}
}
-pub fn find_testable_code(doc: &str, tests: &mut ::test::Collector) {
+pub fn find_testable_code(doc: &str, tests: &mut ::test::Collector, start_line: usize) {
extern fn block(_ob: *mut hoedown_buffer,
text: *const hoedown_buffer,
lang: *const hoedown_buffer,
data: *const hoedown_renderer_data,
- _: libc::size_t) {
+ line: libc::size_t) {
unsafe {
if text.is_null() { return }
let block_info = if lang.is_null() {
stripped_filtered_line(l).unwrap_or(l)
});
let text = lines.collect::<Vec<&str>>().join("\n");
+ let line = tests.get_line() + line;
tests.add_test(text.to_owned(),
block_info.should_panic, block_info.no_run,
block_info.ignore, block_info.test_harness,
block_info.compile_fail, block_info.error_codes,
- block_info.original);
+ line);
}
}
}
}
+ tests.set_line(start_line);
unsafe {
let ob = hoedown_buffer_new(DEF_OUNIT);
let renderer = hoedown_html_renderer_new(0, 0);
let mut opts = TestOptions::default();
opts.no_crate_inject = true;
let mut collector = Collector::new(input.to_string(), cfgs, libs, externs,
- true, opts, maybe_sysroot, &input_str, "input".to_string());
- find_testable_code(&input_str, &mut collector);
+ true, opts, maybe_sysroot, "input".to_string(),
+ None);
+ find_testable_code(&input_str, &mut collector, 0);
test_args.insert(0, "rustdoctest".to_string());
testing::test_main(&test_args, collector.tests);
0
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::collections::HashMap;
use std::env;
use std::ffi::OsString;
-use std::fs::File;
use std::io::prelude::*;
use std::io;
use std::path::PathBuf;
use syntax::ast;
use syntax::codemap::CodeMap;
use syntax::feature_gate::UnstableFeatures;
+use syntax_pos::{BytePos, DUMMY_SP, Pos};
use errors;
use errors::emitter::ColorConfig;
let _ignore = dep_graph.in_ignore();
let cstore = Rc::new(CStore::new(&dep_graph));
let mut sess = session::build_session_(
- sessopts, &dep_graph, Some(input_path.clone()), handler, codemap, cstore.clone(),
+ sessopts, &dep_graph, Some(input_path.clone()), handler, codemap.clone(), cstore.clone(),
);
rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
sess.parse_sess.config =
});
let opts = scrape_test_config(hir_forest.krate());
let filename = input_path.to_str().unwrap_or("").to_owned();
- let mut f = match File::open(input_path) {
- Ok(f) => f,
- _ => return 1,
- };
- let mut file_content = String::new();
- if let Err(_) = f.read_to_string(&mut file_content) {
- return 1;
- }
let mut collector = Collector::new(crate_name,
cfgs,
libs,
false,
opts,
maybe_sysroot,
- &file_content,
- filename);
+ filename,
+ Some(codemap));
{
let dep_graph = DepGraph::new(false);
cratename: String,
opts: TestOptions,
maybe_sysroot: Option<PathBuf>,
- code_blocks: HashMap<String, Vec<u32>>,
filename: String,
+ start_line: usize,
+ codemap: Option<Rc<CodeMap>>,
}
impl Collector {
pub fn new(cratename: String, cfgs: Vec<String>, libs: SearchPaths, externs: Externs,
use_headers: bool, opts: TestOptions, maybe_sysroot: Option<PathBuf>,
- file_content: &str, filename: String) -> Collector {
- let mut line_number = 1;
- let mut block_lines = HashMap::new();
- for (pos, block) in file_content.split("```").enumerate() {
- if (pos & 1) != 0 {
- let key = format!("{}", block.replace("/// ", "").replace("//!", ""));
- if !block_lines.contains_key(&key) {
- block_lines.insert(key.clone(), Vec::new());
- }
- block_lines.get_mut(&key).unwrap().push(line_number);
- }
- line_number += block.lines().count() as u32 - 1;
- }
-
+ filename: String, codemap: Option<Rc<CodeMap>>) -> Collector {
Collector {
tests: Vec::new(),
names: Vec::new(),
cratename: cratename,
opts: opts,
maybe_sysroot: maybe_sysroot,
- code_blocks: block_lines,
filename: filename,
+ start_line: 0,
+ codemap: codemap,
}
}
- fn get_line_from_key(&mut self, key: &String) -> u32 {
- let (line, need_removal) = if let Some(l) = self.code_blocks.get_mut(key) {
- let need_removal = l.len() > 1;
- (l.pop().unwrap_or(1), need_removal)
- } else {
- return 1;
- };
- if need_removal {
- self.code_blocks.remove(key);
- }
- line
- }
-
pub fn add_test(&mut self, test: String,
should_panic: bool, no_run: bool, should_ignore: bool,
as_test_harness: bool, compile_fail: bool, error_codes: Vec<String>,
- original: String) {
- let line_number = self.get_line_from_key(&format!("{}\n{}\n", original, test));
- let name = format!("{} - line {}", self.filename, line_number);
+ line: usize) {
+ let name = format!("{} - line {}", self.filename, line);
self.cnt += 1;
let cfgs = self.cfgs.clone();
let libs = self.libs.clone();
});
}
+ pub fn get_line(&self) -> usize {
+ if let Some(ref codemap) = self.codemap{
+ codemap.lookup_char_pos(BytePos(self.start_line as u32)).line - 1
+ } else {
+ self.start_line
+ }
+ }
+
+ pub fn set_line(&mut self, start_line: usize) {
+ self.start_line = start_line;
+ }
+
pub fn register_header(&mut self, name: &str, level: u32) {
if self.use_headers && level == 1 {
// we use these headings as test names, so it's good if
attrs.unindent_doc_comments();
if let Some(doc) = attrs.doc_value() {
self.collector.cnt = 0;
- markdown::find_testable_code(doc, self.collector);
+ markdown::find_testable_code(doc, self.collector,
+ attrs.span.unwrap_or(DUMMY_SP).lo.to_usize());
}
nested(self);
Symbol::intern("doc"),
Symbol::intern(&strip_doc_comment_decoration(&comment.as_str())));
if self.style == ast::AttrStyle::Outer {
- f(&mk_attr_outer(self.id, meta))
+ f(&mk_attr_outer(self.span, self.id, meta))
} else {
- f(&mk_attr_inner(self.id, meta))
+ f(&mk_attr_inner(self.span, self.id, meta))
}
} else {
f(self)
}
/// Returns an inner attribute with the given value.
-pub fn mk_attr_inner(id: AttrId, item: MetaItem) -> Attribute {
- mk_spanned_attr_inner(DUMMY_SP, id, item)
+pub fn mk_attr_inner(span: Span, id: AttrId, item: MetaItem) -> Attribute {
+ mk_spanned_attr_inner(span, id, item)
}
/// Returns an innter attribute with the given value and span.
/// Returns an outer attribute with the given value.
-pub fn mk_attr_outer(id: AttrId, item: MetaItem) -> Attribute {
- mk_spanned_attr_outer(DUMMY_SP, id, item)
+pub fn mk_attr_outer(span: Span, id: AttrId, item: MetaItem) -> Attribute {
+ mk_spanned_attr_outer(span, id, item)
}
/// Returns an outer attribute with the given value and span.
use ptr::P;
use std_inject;
use symbol::{Symbol, keywords};
+use syntax_pos::DUMMY_SP;
use tokenstream::{self, TokenTree};
use rustc_i128::i128;
// #![feature(prelude_import)]
let prelude_import_meta = attr::mk_list_word_item(Symbol::intern("prelude_import"));
let list = attr::mk_list_item(Symbol::intern("feature"), vec![prelude_import_meta]);
- let fake_attr = attr::mk_attr_inner(attr::mk_attr_id(), list);
+ let fake_attr = attr::mk_attr_inner(DUMMY_SP, attr::mk_attr_id(), list);
s.print_attribute(&fake_attr)?;
// #![no_std]
let no_std_meta = attr::mk_word_item(Symbol::intern("no_std"));
- let fake_attr = attr::mk_attr_inner(attr::mk_attr_id(), no_std_meta);
+ let fake_attr = attr::mk_attr_inner(DUMMY_SP, attr::mk_attr_id(), no_std_meta);
s.print_attribute(&fake_attr)?;
}
let crate_name = Symbol::intern(&alt_std_name.unwrap_or(name.to_string()));
krate.module.items.insert(0, P(ast::Item {
- attrs: vec![attr::mk_attr_outer(attr::mk_attr_id(),
+ attrs: vec![attr::mk_attr_outer(DUMMY_SP,
+ attr::mk_attr_id(),
attr::mk_word_item(Symbol::intern("macro_use")))],
vis: ast::Visibility::Inherited,
node: ast::ItemKind::ExternCrate(Some(crate_name)),
let dead_code_str = Symbol::intern("dead_code");
let word_vec = vec![attr::mk_list_word_item(dead_code_str)];
let allow_dead_code_item = attr::mk_list_item(allow_str, word_vec);
- let allow_dead_code = attr::mk_attr_outer(attr::mk_attr_id(),
+ let allow_dead_code = attr::mk_attr_outer(DUMMY_SP,
+ attr::mk_attr_id(),
allow_dead_code_item);
ast::Item {