Skip to content

Commit 5d6f6e6

Browse files
committed
Auto merge of #47274 - Manishearth:rustdoc-span, r=QuietMisdreavus
Use correct line offsets for doctests Not yet tested. This doesn't handle char positions. It could if I collected a map of char offsets and lines, but this is a bit more work and requires hooking into the parser much more (unsure if it's possible). r? @QuietMisdreavus (fixes #45868)
2 parents 48ab4cd + 44b659a commit 5d6f6e6

File tree

7 files changed

+84
-11
lines changed

7 files changed

+84
-11
lines changed

src/librustc_errors/emitter.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -990,7 +990,7 @@ impl EmitterWriter {
990990
buffer.append(buffer_msg_line_offset,
991991
&format!("{}:{}:{}",
992992
loc.file.name,
993-
loc.line,
993+
cm.doctest_offset_line(loc.line),
994994
loc.col.0 + 1),
995995
Style::LineAndColumn);
996996
for _ in 0..max_line_num_len {
@@ -1000,7 +1000,7 @@ impl EmitterWriter {
10001000
buffer.prepend(0,
10011001
&format!("{}:{}:{} - ",
10021002
loc.file.name,
1003-
loc.line,
1003+
cm.doctest_offset_line(loc.line),
10041004
loc.col.0 + 1),
10051005
Style::LineAndColumn);
10061006
}

src/librustc_errors/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ pub trait CodeMapper {
103103
fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span>;
104104
fn call_span_if_macro(&self, sp: Span) -> Span;
105105
fn ensure_filemap_source_present(&self, file_map: Rc<FileMap>) -> bool;
106+
fn doctest_offset_line(&self, line: usize) -> usize;
106107
}
107108

108109
impl CodeSuggestion {

src/librustdoc/html/markdown.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'a, I> {
196196
.map(|l| map_line(l).for_code())
197197
.collect::<Vec<&str>>().join("\n");
198198
let krate = krate.as_ref().map(|s| &**s);
199-
let test = test::make_test(&test, krate, false,
199+
let (test, _) = test::make_test(&test, krate, false,
200200
&Default::default());
201201
let channel = if test.contains("#![feature(") {
202202
"&amp;version=nightly"
@@ -607,7 +607,7 @@ pub fn render(w: &mut fmt::Formatter,
607607
.map(|l| map_line(l).for_code())
608608
.collect::<Vec<&str>>().join("\n");
609609
let krate = krate.as_ref().map(|s| &**s);
610-
let test = test::make_test(&test, krate, false,
610+
let (test, _) = test::make_test(&test, krate, false,
611611
&Default::default());
612612
let channel = if test.contains("#![feature(") {
613613
"&amp;version=nightly"

src/librustdoc/test.rs

+15-6
Original file line numberDiff line numberDiff line change
@@ -176,15 +176,16 @@ fn scrape_test_config(krate: &::rustc::hir::Crate) -> TestOptions {
176176
opts
177177
}
178178

179-
fn run_test(test: &str, cratename: &str, filename: &FileName, cfgs: Vec<String>, libs: SearchPaths,
179+
fn run_test(test: &str, cratename: &str, filename: &FileName, line: usize,
180+
cfgs: Vec<String>, libs: SearchPaths,
180181
externs: Externs,
181182
should_panic: bool, no_run: bool, as_test_harness: bool,
182183
compile_fail: bool, mut error_codes: Vec<String>, opts: &TestOptions,
183184
maybe_sysroot: Option<PathBuf>,
184185
linker: Option<PathBuf>) {
185186
// the test harness wants its own `main` & top level functions, so
186187
// never wrap the test in `fn main() { ... }`
187-
let test = make_test(test, Some(cratename), as_test_harness, opts);
188+
let (test, line_offset) = make_test(test, Some(cratename), as_test_harness, opts);
188189
// FIXME(#44940): if doctests ever support path remapping, then this filename
189190
// needs to be the result of CodeMap::span_to_unmapped_path
190191
let input = config::Input::Str {
@@ -234,7 +235,9 @@ fn run_test(test: &str, cratename: &str, filename: &FileName, cfgs: Vec<String>,
234235
}
235236
}
236237
let data = Arc::new(Mutex::new(Vec::new()));
237-
let codemap = Rc::new(CodeMap::new(sessopts.file_path_mapping()));
238+
let codemap = Rc::new(CodeMap::new_doctest(
239+
sessopts.file_path_mapping(), filename.clone(), line as isize - line_offset as isize
240+
));
238241
let emitter = errors::emitter::EmitterWriter::new(box Sink(data.clone()),
239242
Some(codemap.clone()),
240243
false);
@@ -326,13 +329,14 @@ fn run_test(test: &str, cratename: &str, filename: &FileName, cfgs: Vec<String>,
326329
}
327330
}
328331

332+
/// Makes the test file. Also returns the number of lines before the code begins
329333
pub fn make_test(s: &str,
330334
cratename: Option<&str>,
331335
dont_insert_main: bool,
332336
opts: &TestOptions)
333-
-> String {
337+
-> (String, usize) {
334338
let (crate_attrs, everything_else) = partition_source(s);
335-
339+
let mut line_offset = 0;
336340
let mut prog = String::new();
337341

338342
if opts.attrs.is_empty() {
@@ -341,11 +345,13 @@ pub fn make_test(s: &str,
341345
// commonly used to make tests fail in case they trigger warnings, so having this there in
342346
// that case may cause some tests to pass when they shouldn't have.
343347
prog.push_str("#![allow(unused)]\n");
348+
line_offset += 1;
344349
}
345350

346351
// Next, any attributes that came from the crate root via #![doc(test(attr(...)))].
347352
for attr in &opts.attrs {
348353
prog.push_str(&format!("#![{}]\n", attr));
354+
line_offset += 1;
349355
}
350356

351357
// Now push any outer attributes from the example, assuming they
@@ -358,6 +364,7 @@ pub fn make_test(s: &str,
358364
if let Some(cratename) = cratename {
359365
if s.contains(cratename) {
360366
prog.push_str(&format!("extern crate {};\n", cratename));
367+
line_offset += 1;
361368
}
362369
}
363370
}
@@ -379,14 +386,15 @@ pub fn make_test(s: &str,
379386
prog.push_str(&everything_else);
380387
} else {
381388
prog.push_str("fn main() {\n");
389+
line_offset += 1;
382390
prog.push_str(&everything_else);
383391
prog = prog.trim().into();
384392
prog.push_str("\n}");
385393
}
386394

387395
info!("final test program: {}", prog);
388396

389-
prog
397+
(prog, line_offset)
390398
}
391399

392400
// FIXME(aburka): use a real parser to deal with multiline attributes
@@ -543,6 +551,7 @@ impl Collector {
543551
run_test(&test,
544552
&cratename,
545553
&filename,
554+
line,
546555
cfgs,
547556
libs,
548557
externs,

src/libsyntax/codemap.rs

+35-1
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ pub struct CodeMap {
131131
// -Zremap-path-prefix to all FileMaps allocated within this CodeMap.
132132
path_mapping: FilePathMapping,
133133
stable_id_to_filemap: RefCell<FxHashMap<StableFilemapId, Rc<FileMap>>>,
134+
/// In case we are in a doctest, replace all file names with the PathBuf,
135+
/// and add the given offsets to the line info
136+
doctest_offset: Option<(FileName, isize)>,
134137
}
135138

136139
impl CodeMap {
@@ -140,9 +143,19 @@ impl CodeMap {
140143
file_loader: Box::new(RealFileLoader),
141144
path_mapping,
142145
stable_id_to_filemap: RefCell::new(FxHashMap()),
146+
doctest_offset: None,
143147
}
144148
}
145149

150+
pub fn new_doctest(path_mapping: FilePathMapping,
151+
file: FileName, line: isize) -> CodeMap {
152+
CodeMap {
153+
doctest_offset: Some((file, line)),
154+
..CodeMap::new(path_mapping)
155+
}
156+
157+
}
158+
146159
pub fn with_file_loader(file_loader: Box<FileLoader>,
147160
path_mapping: FilePathMapping)
148161
-> CodeMap {
@@ -151,6 +164,7 @@ impl CodeMap {
151164
file_loader,
152165
path_mapping,
153166
stable_id_to_filemap: RefCell::new(FxHashMap()),
167+
doctest_offset: None,
154168
}
155169
}
156170

@@ -164,7 +178,12 @@ impl CodeMap {
164178

165179
pub fn load_file(&self, path: &Path) -> io::Result<Rc<FileMap>> {
166180
let src = self.file_loader.read_file(path)?;
167-
Ok(self.new_filemap(path.to_owned().into(), src))
181+
let filename = if let Some((ref name, _)) = self.doctest_offset {
182+
name.clone()
183+
} else {
184+
path.to_owned().into()
185+
};
186+
Ok(self.new_filemap(filename, src))
168187
}
169188

170189
pub fn files(&self) -> Ref<Vec<Rc<FileMap>>> {
@@ -303,6 +322,18 @@ impl CodeMap {
303322
pos.col.to_usize() + 1)).to_string()
304323
}
305324

325+
// If there is a doctest_offset, apply it to the line
326+
pub fn doctest_offset_line(&self, mut orig: usize) -> usize {
327+
if let Some((_, line)) = self.doctest_offset {
328+
if line >= 0 {
329+
orig = orig + line as usize;
330+
} else {
331+
orig = orig - (-line) as usize;
332+
}
333+
}
334+
orig
335+
}
336+
306337
/// Lookup source information about a BytePos
307338
pub fn lookup_char_pos(&self, pos: BytePos) -> Loc {
308339
let chpos = self.bytepos_to_file_charpos(pos);
@@ -681,6 +712,9 @@ impl CodeMapper for CodeMap {
681712
}
682713
)
683714
}
715+
fn doctest_offset_line(&self, line: usize) -> usize {
716+
self.doctest_offset_line(line)
717+
}
684718
}
685719

686720
#[derive(Clone)]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
-include ../tools.mk
2+
3+
# Test that hir-tree output doens't crash and includes
4+
# the string constant we would expect to see.
5+
6+
all:
7+
$(RUSTDOC) --test input.rs > $(TMPDIR)/output || true
8+
$(CGREP) 'input.rs:17:15' < $(TMPDIR)/output
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test for #45868
12+
13+
// random #![feature] to ensure that crate attrs
14+
// do not offset things
15+
/// ```rust
16+
/// #![feature(nll)]
17+
/// let x: char = 1;
18+
/// ```
19+
pub fn foo() {
20+
21+
}

0 commit comments

Comments
 (0)