Skip to content

Commit cae1f4d

Browse files
committed
Auto merge of #79762 - Swatinem:remap-doctest-coverage, r=Swatinem
Remap instrument-coverage line numbers in doctests This uses the `SourceMap::doctest_offset_line` method to re-map line numbers from doctests. Remapping columns is not yet done, and rustdoc still does not output the correct filename when running doctests in a workspace. Part of #79417 although I dont consider that fixed until both filenames and columns are mapped correctly. r? `@richkadel` I might jump on zulip the comming days. Still need to figure out how to properly write tests for this, and deal with other doctest issues in the meantime.
2 parents 2c308b9 + 087101e commit cae1f4d

File tree

15 files changed

+599
-56
lines changed

15 files changed

+599
-56
lines changed

compiler/rustc_mir/src/transform/coverage/mod.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use rustc_middle::mir::{
3030
};
3131
use rustc_middle::ty::TyCtxt;
3232
use rustc_span::def_id::DefId;
33+
use rustc_span::source_map::SourceMap;
3334
use rustc_span::{CharPos, Pos, SourceFile, Span, Symbol};
3435

3536
/// A simple error message wrapper for `coverage::Error`s.
@@ -311,7 +312,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
311312
self.mir_body,
312313
counter_kind,
313314
self.bcb_leader_bb(bcb),
314-
Some(make_code_region(file_name, &self.source_file, span, body_span)),
315+
Some(make_code_region(source_map, file_name, &self.source_file, span, body_span)),
315316
);
316317
}
317318
}
@@ -489,6 +490,7 @@ fn inject_intermediate_expression(mir_body: &mut mir::Body<'tcx>, expression: Co
489490

490491
/// Convert the Span into its file name, start line and column, and end line and column
491492
fn make_code_region(
493+
source_map: &SourceMap,
492494
file_name: Symbol,
493495
source_file: &Lrc<SourceFile>,
494496
span: Span,
@@ -508,6 +510,8 @@ fn make_code_region(
508510
} else {
509511
source_file.lookup_file_pos(span.hi())
510512
};
513+
let start_line = source_map.doctest_offset_line(&source_file.name, start_line);
514+
let end_line = source_map.doctest_offset_line(&source_file.name, end_line);
511515
CodeRegion {
512516
file_name,
513517
start_line: start_line as u32,

compiler/rustc_span/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ impl std::fmt::Display for FileName {
182182
use FileName::*;
183183
match *self {
184184
Real(RealFileName::Named(ref path)) => write!(fmt, "{}", path.display()),
185-
// FIXME: might be nice to display both compoments of Devirtualized.
185+
// FIXME: might be nice to display both components of Devirtualized.
186186
// But for now (to backport fix for issue #70924), best to not
187187
// perturb diagnostics so its obvious test suite still works.
188188
Real(RealFileName::Devirtualized { ref local_path, virtual_name: _ }) => {

src/librustdoc/doctest.rs

+50-26
Original file line numberDiff line numberDiff line change
@@ -247,9 +247,10 @@ fn run_test(
247247
edition: Edition,
248248
outdir: DirState,
249249
path: PathBuf,
250+
test_id: &str,
250251
) -> Result<(), TestFailure> {
251252
let (test, line_offset, supports_color) =
252-
make_test(test, Some(cratename), as_test_harness, opts, edition);
253+
make_test(test, Some(cratename), as_test_harness, opts, edition, Some(test_id));
253254

254255
let output_file = outdir.path().join("rust_out");
255256

@@ -387,6 +388,7 @@ crate fn make_test(
387388
dont_insert_main: bool,
388389
opts: &TestOptions,
389390
edition: Edition,
391+
test_id: Option<&str>,
390392
) -> (String, usize, bool) {
391393
let (crate_attrs, everything_else, crates) = partition_source(s);
392394
let everything_else = everything_else.trim();
@@ -542,16 +544,41 @@ crate fn make_test(
542544
prog.push_str(everything_else);
543545
} else {
544546
let returns_result = everything_else.trim_end().ends_with("(())");
547+
// Give each doctest main function a unique name.
548+
// This is for example needed for the tooling around `-Z instrument-coverage`.
549+
let inner_fn_name = if let Some(test_id) = test_id {
550+
format!("_doctest_main_{}", test_id)
551+
} else {
552+
"_inner".into()
553+
};
554+
let inner_attr = if test_id.is_some() { "#[allow(non_snake_case)] " } else { "" };
545555
let (main_pre, main_post) = if returns_result {
546556
(
547-
"fn main() { fn _inner() -> Result<(), impl core::fmt::Debug> {",
548-
"}\n_inner().unwrap() }",
557+
format!(
558+
"fn main() {{ {}fn {}() -> Result<(), impl core::fmt::Debug> {{\n",
559+
inner_attr, inner_fn_name
560+
),
561+
format!("\n}}; {}().unwrap() }}", inner_fn_name),
562+
)
563+
} else if test_id.is_some() {
564+
(
565+
format!("fn main() {{ {}fn {}() {{\n", inner_attr, inner_fn_name),
566+
format!("\n}}; {}() }}", inner_fn_name),
549567
)
550568
} else {
551-
("fn main() {\n", "\n}")
569+
("fn main() {\n".into(), "\n}".into())
552570
};
553-
prog.extend([main_pre, everything_else, main_post].iter().cloned());
571+
// Note on newlines: We insert a line/newline *before*, and *after*
572+
// the doctest and adjust the `line_offset` accordingly.
573+
// In the case of `-Z instrument-coverage`, this means that the generated
574+
// inner `main` function spans from the doctest opening codeblock to the
575+
// closing one. For example
576+
// /// ``` <- start of the inner main
577+
// /// <- code under doctest
578+
// /// ``` <- end of the inner main
554579
line_offset += 1;
580+
581+
prog.extend([&main_pre, everything_else, &main_post].iter().cloned());
555582
}
556583

557584
debug!("final doctest:\n{}", prog);
@@ -749,28 +776,24 @@ impl Tester for Collector {
749776
_ => PathBuf::from(r"doctest.rs"),
750777
};
751778

779+
// For example `module/file.rs` would become `module_file_rs`
780+
let file = filename
781+
.to_string()
782+
.chars()
783+
.map(|c| if c.is_ascii_alphanumeric() { c } else { '_' })
784+
.collect::<String>();
785+
let test_id = format!(
786+
"{file}_{line}_{number}",
787+
file = file,
788+
line = line,
789+
number = {
790+
// Increases the current test number, if this file already
791+
// exists or it creates a new entry with a test number of 0.
792+
self.visited_tests.entry((file.clone(), line)).and_modify(|v| *v += 1).or_insert(0)
793+
},
794+
);
752795
let outdir = if let Some(mut path) = options.persist_doctests.clone() {
753-
// For example `module/file.rs` would become `module_file_rs`
754-
let folder_name = filename
755-
.to_string()
756-
.chars()
757-
.map(|c| if c == '\\' || c == '/' || c == '.' { '_' } else { c })
758-
.collect::<String>();
759-
760-
path.push(format!(
761-
"{krate}_{file}_{line}_{number}",
762-
krate = cratename,
763-
file = folder_name,
764-
line = line,
765-
number = {
766-
// Increases the current test number, if this file already
767-
// exists or it creates a new entry with a test number of 0.
768-
self.visited_tests
769-
.entry((folder_name.clone(), line))
770-
.and_modify(|v| *v += 1)
771-
.or_insert(0)
772-
},
773-
));
796+
path.push(&test_id);
774797

775798
std::fs::create_dir_all(&path)
776799
.expect("Couldn't create directory for doctest executables");
@@ -817,6 +840,7 @@ impl Tester for Collector {
817840
edition,
818841
outdir,
819842
path,
843+
&test_id,
820844
);
821845

822846
if let Err(err) = res {

src/librustdoc/doctest/tests.rs

+52-17
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ fn main() {
1111
assert_eq!(2+2, 4);
1212
}"
1313
.to_string();
14-
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION);
14+
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None);
1515
assert_eq!((output, len), (expected, 2));
1616
}
1717

@@ -26,7 +26,7 @@ fn main() {
2626
assert_eq!(2+2, 4);
2727
}"
2828
.to_string();
29-
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
29+
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None);
3030
assert_eq!((output, len), (expected, 2));
3131
}
3232

@@ -44,7 +44,7 @@ use asdf::qwop;
4444
assert_eq!(2+2, 4);
4545
}"
4646
.to_string();
47-
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
47+
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None);
4848
assert_eq!((output, len), (expected, 3));
4949
}
5050

@@ -61,7 +61,7 @@ use asdf::qwop;
6161
assert_eq!(2+2, 4);
6262
}"
6363
.to_string();
64-
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
64+
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None);
6565
assert_eq!((output, len), (expected, 2));
6666
}
6767

@@ -79,7 +79,7 @@ use std::*;
7979
assert_eq!(2+2, 4);
8080
}"
8181
.to_string();
82-
let (output, len, _) = make_test(input, Some("std"), false, &opts, DEFAULT_EDITION);
82+
let (output, len, _) = make_test(input, Some("std"), false, &opts, DEFAULT_EDITION, None);
8383
assert_eq!((output, len), (expected, 2));
8484
}
8585

@@ -98,7 +98,7 @@ use asdf::qwop;
9898
assert_eq!(2+2, 4);
9999
}"
100100
.to_string();
101-
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
101+
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None);
102102
assert_eq!((output, len), (expected, 2));
103103
}
104104

@@ -115,7 +115,7 @@ use asdf::qwop;
115115
assert_eq!(2+2, 4);
116116
}"
117117
.to_string();
118-
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
118+
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None);
119119
assert_eq!((output, len), (expected, 2));
120120
}
121121

@@ -134,7 +134,7 @@ use asdf::qwop;
134134
assert_eq!(2+2, 4);
135135
}"
136136
.to_string();
137-
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
137+
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None);
138138
assert_eq!((output, len), (expected, 3));
139139

140140
// Adding more will also bump the returned line offset.
@@ -147,7 +147,7 @@ use asdf::qwop;
147147
assert_eq!(2+2, 4);
148148
}"
149149
.to_string();
150-
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
150+
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None);
151151
assert_eq!((output, len), (expected, 4));
152152
}
153153

@@ -164,7 +164,7 @@ fn main() {
164164
assert_eq!(2+2, 4);
165165
}"
166166
.to_string();
167-
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION);
167+
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None);
168168
assert_eq!((output, len), (expected, 2));
169169
}
170170

@@ -180,7 +180,7 @@ fn main() {
180180
assert_eq!(2+2, 4);
181181
}"
182182
.to_string();
183-
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION);
183+
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None);
184184
assert_eq!((output, len), (expected, 1));
185185
}
186186

@@ -196,7 +196,7 @@ fn main() {
196196
assert_eq!(2+2, 4);
197197
}"
198198
.to_string();
199-
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION);
199+
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None);
200200
assert_eq!((output, len), (expected, 2));
201201
}
202202

@@ -210,7 +210,7 @@ assert_eq!(2+2, 4);";
210210
//Ceci n'est pas une `fn main`
211211
assert_eq!(2+2, 4);"
212212
.to_string();
213-
let (output, len, _) = make_test(input, None, true, &opts, DEFAULT_EDITION);
213+
let (output, len, _) = make_test(input, None, true, &opts, DEFAULT_EDITION, None);
214214
assert_eq!((output, len), (expected, 1));
215215
}
216216

@@ -224,7 +224,7 @@ fn make_test_display_warnings() {
224224
assert_eq!(2+2, 4);
225225
}"
226226
.to_string();
227-
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION);
227+
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None);
228228
assert_eq!((output, len), (expected, 1));
229229
}
230230

@@ -242,7 +242,7 @@ assert_eq!(2+2, 4);
242242
}"
243243
.to_string();
244244

245-
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION);
245+
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None);
246246
assert_eq!((output, len), (expected, 2));
247247

248248
let input = "extern crate hella_qwop;
@@ -256,7 +256,7 @@ assert_eq!(asdf::foo, 4);
256256
}"
257257
.to_string();
258258

259-
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
259+
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None);
260260
assert_eq!((output, len), (expected, 3));
261261
}
262262

@@ -274,6 +274,41 @@ test_wrapper! {
274274
}"
275275
.to_string();
276276

277-
let (output, len, _) = make_test(input, Some("my_crate"), false, &opts, DEFAULT_EDITION);
277+
let (output, len, _) = make_test(input, Some("my_crate"), false, &opts, DEFAULT_EDITION, None);
278278
assert_eq!((output, len), (expected, 1));
279279
}
280+
281+
#[test]
282+
fn make_test_returns_result() {
283+
// creates an inner function and unwraps it
284+
let opts = TestOptions::default();
285+
let input = "use std::io;
286+
let mut input = String::new();
287+
io::stdin().read_line(&mut input)?;
288+
Ok::<(), io:Error>(())";
289+
let expected = "#![allow(unused)]
290+
fn main() { fn _inner() -> Result<(), impl core::fmt::Debug> {
291+
use std::io;
292+
let mut input = String::new();
293+
io::stdin().read_line(&mut input)?;
294+
Ok::<(), io:Error>(())
295+
}; _inner().unwrap() }"
296+
.to_string();
297+
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None);
298+
assert_eq!((output, len), (expected, 2));
299+
}
300+
301+
#[test]
302+
fn make_test_named_wrapper() {
303+
// creates an inner function with a specific name
304+
let opts = TestOptions::default();
305+
let input = "assert_eq!(2+2, 4);";
306+
let expected = "#![allow(unused)]
307+
fn main() { #[allow(non_snake_case)] fn _doctest_main__some_unique_name() {
308+
assert_eq!(2+2, 4);
309+
}; _doctest_main__some_unique_name() }"
310+
.to_string();
311+
let (output, len, _) =
312+
make_test(input, None, false, &opts, DEFAULT_EDITION, Some("_some_unique_name"));
313+
assert_eq!((output, len), (expected, 2));
314+
}

src/librustdoc/html/markdown.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
248248
.join("\n");
249249
let krate = krate.as_ref().map(|s| &**s);
250250
let (test, _, _) =
251-
doctest::make_test(&test, krate, false, &Default::default(), edition);
251+
doctest::make_test(&test, krate, false, &Default::default(), edition, None);
252252
let channel = if test.contains("#![feature(") { "&amp;version=nightly" } else { "" };
253253

254254
let edition_string = format!("&amp;edition={}", edition);

0 commit comments

Comments
 (0)