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.

Part of issue #79417.
This commit is contained in:
Arpad Borsos 2020-12-06 13:57:37 +01:00
parent 3d9ada686f
commit 830ceaa419
15 changed files with 596 additions and 56 deletions

View File

@ -30,6 +30,7 @@ use rustc_middle::mir::{
};
use rustc_middle::ty::TyCtxt;
use rustc_span::def_id::DefId;
use rustc_span::source_map::SourceMap;
use rustc_span::{CharPos, Pos, SourceFile, Span, Symbol};
/// A simple error message wrapper for `coverage::Error`s.
@ -311,7 +312,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
self.mir_body,
counter_kind,
self.bcb_leader_bb(bcb),
Some(make_code_region(file_name, &self.source_file, span, body_span)),
Some(make_code_region(source_map, file_name, &self.source_file, span, body_span)),
);
}
}
@ -489,6 +490,7 @@ fn inject_intermediate_expression(mir_body: &mut mir::Body<'tcx>, expression: Co
/// Convert the Span into its file name, start line and column, and end line and column
fn make_code_region(
source_map: &SourceMap,
file_name: Symbol,
source_file: &Lrc<SourceFile>,
span: Span,
@ -508,6 +510,8 @@ fn make_code_region(
} else {
source_file.lookup_file_pos(span.hi())
};
let start_line = source_map.doctest_offset_line(&source_file.name, start_line);
let end_line = source_map.doctest_offset_line(&source_file.name, end_line);
CodeRegion {
file_name,
start_line: start_line as u32,

View File

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

View File

@ -247,9 +247,10 @@ fn run_test(
edition: Edition,
outdir: DirState,
path: PathBuf,
test_id: &str,
) -> Result<(), TestFailure> {
let (test, line_offset, supports_color) =
make_test(test, Some(cratename), as_test_harness, opts, edition);
make_test(test, Some(cratename), as_test_harness, opts, edition, Some(test_id));
let output_file = outdir.path().join("rust_out");
@ -387,6 +388,7 @@ crate fn make_test(
dont_insert_main: bool,
opts: &TestOptions,
edition: Edition,
test_id: Option<&str>,
) -> (String, usize, bool) {
let (crate_attrs, everything_else, crates) = partition_source(s);
let everything_else = everything_else.trim();
@ -542,16 +544,40 @@ crate fn make_test(
prog.push_str(everything_else);
} else {
let returns_result = everything_else.trim_end().ends_with("(())");
// Give each doctest main function a unique name.
// This is for example needed for the tooling around `-Z instrument-coverage`.
let inner_fn_name = if let Some(test_id) = test_id {
format!("_doctest_main_{}", test_id)
} else {
"_inner".into()
};
let (main_pre, main_post) = if returns_result {
(
"fn main() { fn _inner() -> Result<(), impl core::fmt::Debug> {",
"}\n_inner().unwrap() }",
format!(
"fn main() {{ fn {}() -> Result<(), impl core::fmt::Debug> {{\n",
inner_fn_name
),
format!("\n}}; {}().unwrap() }}", inner_fn_name),
)
} else if test_id.is_some() {
(
format!("fn main() {{ fn {}() {{\n", inner_fn_name),
format!("\n}}; {}() }}", inner_fn_name),
)
} else {
("fn main() {\n", "\n}")
("fn main() {\n".into(), "\n}".into())
};
prog.extend([main_pre, everything_else, main_post].iter().cloned());
// Note on newlines: We insert a line/newline *before*, and *after*
// the doctest and adjust the `line_offset` accordingly.
// In the case of `-Z instrument-coverage`, this means that the generated
// inner `main` function spans from the doctest opening codeblock to the
// closing one. For example
// /// ``` <- start of the inner main
// /// <- code under doctest
// /// ``` <- end of the inner main
line_offset += 1;
prog.extend([&main_pre, everything_else, &main_post].iter().cloned());
}
debug!("final doctest:\n{}", prog);
@ -749,28 +775,24 @@ impl Tester for Collector {
_ => PathBuf::from(r"doctest.rs"),
};
// For example `module/file.rs` would become `module_file_rs`
let file = filename
.to_string()
.chars()
.map(|c| if c.is_ascii_alphanumeric() { c } else { '_' })
.collect::<String>();
let test_id = format!(
"{file}_{line}_{number}",
file = file,
line = line,
number = {
// Increases the current test number, if this file already
// exists or it creates a new entry with a test number of 0.
self.visited_tests.entry((file.clone(), line)).and_modify(|v| *v += 1).or_insert(0)
},
);
let outdir = if let Some(mut path) = options.persist_doctests.clone() {
// For example `module/file.rs` would become `module_file_rs`
let folder_name = filename
.to_string()
.chars()
.map(|c| if c == '\\' || c == '/' || c == '.' { '_' } else { c })
.collect::<String>();
path.push(format!(
"{krate}_{file}_{line}_{number}",
krate = cratename,
file = folder_name,
line = line,
number = {
// Increases the current test number, if this file already
// exists or it creates a new entry with a test number of 0.
self.visited_tests
.entry((folder_name.clone(), line))
.and_modify(|v| *v += 1)
.or_insert(0)
},
));
path.push(&test_id);
std::fs::create_dir_all(&path)
.expect("Couldn't create directory for doctest executables");
@ -817,6 +839,7 @@ impl Tester for Collector {
edition,
outdir,
path,
&test_id,
);
if let Err(err) = res {

View File

@ -11,7 +11,7 @@ fn main() {
assert_eq!(2+2, 4);
}"
.to_string();
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION);
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None);
assert_eq!((output, len), (expected, 2));
}
@ -26,7 +26,7 @@ fn main() {
assert_eq!(2+2, 4);
}"
.to_string();
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None);
assert_eq!((output, len), (expected, 2));
}
@ -44,7 +44,7 @@ use asdf::qwop;
assert_eq!(2+2, 4);
}"
.to_string();
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None);
assert_eq!((output, len), (expected, 3));
}
@ -61,7 +61,7 @@ use asdf::qwop;
assert_eq!(2+2, 4);
}"
.to_string();
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None);
assert_eq!((output, len), (expected, 2));
}
@ -79,7 +79,7 @@ use std::*;
assert_eq!(2+2, 4);
}"
.to_string();
let (output, len, _) = make_test(input, Some("std"), false, &opts, DEFAULT_EDITION);
let (output, len, _) = make_test(input, Some("std"), false, &opts, DEFAULT_EDITION, None);
assert_eq!((output, len), (expected, 2));
}
@ -98,7 +98,7 @@ use asdf::qwop;
assert_eq!(2+2, 4);
}"
.to_string();
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None);
assert_eq!((output, len), (expected, 2));
}
@ -115,7 +115,7 @@ use asdf::qwop;
assert_eq!(2+2, 4);
}"
.to_string();
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None);
assert_eq!((output, len), (expected, 2));
}
@ -134,7 +134,7 @@ use asdf::qwop;
assert_eq!(2+2, 4);
}"
.to_string();
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None);
assert_eq!((output, len), (expected, 3));
// Adding more will also bump the returned line offset.
@ -147,7 +147,7 @@ use asdf::qwop;
assert_eq!(2+2, 4);
}"
.to_string();
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None);
assert_eq!((output, len), (expected, 4));
}
@ -164,7 +164,7 @@ fn main() {
assert_eq!(2+2, 4);
}"
.to_string();
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION);
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None);
assert_eq!((output, len), (expected, 2));
}
@ -180,7 +180,7 @@ fn main() {
assert_eq!(2+2, 4);
}"
.to_string();
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION);
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None);
assert_eq!((output, len), (expected, 1));
}
@ -196,7 +196,7 @@ fn main() {
assert_eq!(2+2, 4);
}"
.to_string();
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION);
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None);
assert_eq!((output, len), (expected, 2));
}
@ -210,7 +210,7 @@ assert_eq!(2+2, 4);";
//Ceci n'est pas une `fn main`
assert_eq!(2+2, 4);"
.to_string();
let (output, len, _) = make_test(input, None, true, &opts, DEFAULT_EDITION);
let (output, len, _) = make_test(input, None, true, &opts, DEFAULT_EDITION, None);
assert_eq!((output, len), (expected, 1));
}
@ -224,7 +224,7 @@ fn make_test_display_warnings() {
assert_eq!(2+2, 4);
}"
.to_string();
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION);
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None);
assert_eq!((output, len), (expected, 1));
}
@ -242,7 +242,7 @@ assert_eq!(2+2, 4);
}"
.to_string();
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION);
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None);
assert_eq!((output, len), (expected, 2));
let input = "extern crate hella_qwop;
@ -256,7 +256,7 @@ assert_eq!(asdf::foo, 4);
}"
.to_string();
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION);
let (output, len, _) = make_test(input, Some("asdf"), false, &opts, DEFAULT_EDITION, None);
assert_eq!((output, len), (expected, 3));
}
@ -274,6 +274,41 @@ test_wrapper! {
}"
.to_string();
let (output, len, _) = make_test(input, Some("my_crate"), false, &opts, DEFAULT_EDITION);
let (output, len, _) = make_test(input, Some("my_crate"), false, &opts, DEFAULT_EDITION, None);
assert_eq!((output, len), (expected, 1));
}
#[test]
fn make_test_returns_result() {
// creates an inner function and unwraps it
let opts = TestOptions::default();
let input = "use std::io;
let mut input = String::new();
io::stdin().read_line(&mut input)?;
Ok::<(), io:Error>(())";
let expected = "#![allow(unused)]
fn main() { fn _inner() -> Result<(), impl core::fmt::Debug> {
use std::io;
let mut input = String::new();
io::stdin().read_line(&mut input)?;
Ok::<(), io:Error>(())
}; _inner().unwrap() }"
.to_string();
let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None);
assert_eq!((output, len), (expected, 2));
}
#[test]
fn make_test_named_wrapper() {
// creates an inner function with a specific name
let opts = TestOptions::default();
let input = "assert_eq!(2+2, 4);";
let expected = "#![allow(unused)]
fn main() { fn _doctest_main_some_unique_name() {
assert_eq!(2+2, 4);
}; _doctest_main_some_unique_name() }"
.to_string();
let (output, len, _) =
make_test(input, None, false, &opts, DEFAULT_EDITION, Some("some_unique_name"));
assert_eq!((output, len), (expected, 2));
}

View File

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

View File

@ -98,7 +98,7 @@ endif
# Run it in order to generate some profiling data,
# with `LLVM_PROFILE_FILE=<profdata_file>` environment variable set to
# output the coverage stats for this run.
LLVM_PROFILE_FILE="$(TMPDIR)"/$@.profraw \
LLVM_PROFILE_FILE="$(TMPDIR)"/$@-%p.profraw \
$(call RUN,$@) || \
( \
status=$$?; \
@ -108,9 +108,16 @@ endif
) \
)
# Run it through rustdoc as well to cover doctests
LLVM_PROFILE_FILE="$(TMPDIR)"/$@-%p.profraw \
$(RUSTDOC) --crate-name workaround_for_79771 --test $(SOURCEDIR)/$@.rs \
$$( grep -q '^\/\/ require-rust-edition-2018' $(SOURCEDIR)/$@.rs && echo "--edition=2018" ) \
-L "$(TMPDIR)" -Zinstrument-coverage \
-Z unstable-options --persist-doctests=$(TMPDIR)/rustdoc-$@
# Postprocess the profiling data so it can be used by the llvm-cov tool
"$(LLVM_BIN_DIR)"/llvm-profdata merge --sparse \
"$(TMPDIR)"/$@.profraw \
"$(TMPDIR)"/$@-*.profraw \
-o "$(TMPDIR)"/$@.profdata
# Generate a coverage report using `llvm-cov show`.
@ -121,8 +128,15 @@ endif
--show-line-counts-or-regions \
--instr-profile="$(TMPDIR)"/$@.profdata \
$(call BIN,"$(TMPDIR)"/$@) \
> "$(TMPDIR)"/actual_show_coverage.$@.txt \
2> "$(TMPDIR)"/show_coverage_stderr.$@.txt || \
$$( \
for file in $(TMPDIR)/rustdoc-$@/*/rust_out; \
do \
[[ -x $$file ]] && printf "%s %s " -object $$file; \
done \
) \
2> "$(TMPDIR)"/show_coverage_stderr.$@.txt \
| "$(PYTHON)" $(BASEDIR)/normalize_paths.py \
> "$(TMPDIR)"/actual_show_coverage.$@.txt || \
( status=$$? ; \
>&2 cat "$(TMPDIR)"/show_coverage_stderr.$@.txt ; \
exit $$status \

View File

@ -0,0 +1,79 @@
../coverage/doctest.rs:
1| |//! This test ensures that code from doctests is properly re-mapped.
2| |//! See <https://github.com/rust-lang/rust/issues/79417> for more info.
3| |//!
4| |//! Just some random code:
5| 1|//! ```
6| 1|//! if true {
7| |//! // this is executed!
8| 1|//! assert_eq!(1, 1);
9| |//! } else {
10| |//! // this is not!
11| |//! assert_eq!(1, 2);
12| |//! }
13| 1|//! ```
14| |//!
15| |//! doctest testing external code:
16| |//! ```
17| 1|//! extern crate doctest_crate;
18| 1|//! doctest_crate::fn_run_in_doctests(1);
19| 1|//! ```
20| |//!
21| |//! doctest returning a result:
22| 1|//! ```
23| 1|//! #[derive(Debug)]
24| 1|//! struct SomeError;
25| 1|//! let mut res = Err(SomeError);
26| 1|//! if res.is_ok() {
27| 0|//! res?;
28| 1|//! } else {
29| 1|//! res = Ok(0);
30| 1|//! }
31| |//! // need to be explicit because rustdoc cant infer the return type
32| 1|//! Ok::<(), SomeError>(())
33| 1|//! ```
34| |//!
35| |//! doctest with custom main:
36| |//! ```
37| |//! #[derive(Debug)]
38| |//! struct SomeError;
39| |//!
40| |//! extern crate doctest_crate;
41| |//!
42| 1|//! fn doctest_main() -> Result<(), SomeError> {
43| 1|//! doctest_crate::fn_run_in_doctests(2);
44| 1|//! Ok(())
45| 1|//! }
46| |//!
47| |//! // this `main` is not shown as covered, as it clashes with all the other
48| |//! // `main` functions that were automatically generated for doctests
49| |//! fn main() -> Result<(), SomeError> {
50| |//! doctest_main()
51| |//! }
52| |//! ```
53| |
54| |/// doctest attached to fn testing external code:
55| |/// ```
56| 1|/// extern crate doctest_crate;
57| 1|/// doctest_crate::fn_run_in_doctests(3);
58| 1|/// ```
59| |///
60| 1|fn main() {
61| 1| if true {
62| 1| assert_eq!(1, 1);
63| | } else {
64| | assert_eq!(1, 2);
65| | }
66| 1|}
../coverage/lib/doctest_crate.rs:
1| |/// A function run only from within doctests
2| 3|pub fn fn_run_in_doctests(conditional: usize) {
3| 3| match conditional {
4| 1| 1 => assert_eq!(1, 1), // this is run,
5| 1| 2 => assert_eq!(1, 1), // this,
6| 1| 3 => assert_eq!(1, 1), // and this too
7| 0| _ => assert_eq!(1, 2), // however this is not
8| | }
9| 3|}

View File

@ -19,12 +19,12 @@
18| 2| println!("used_only_from_bin_crate_generic_function with {:?}", arg);
19| 2|}
------------------
| used_crate::used_only_from_bin_crate_generic_function::<&str>:
| used_crate::used_only_from_bin_crate_generic_function::<&alloc::vec::Vec<i32>>:
| 17| 1|pub fn used_only_from_bin_crate_generic_function<T: Debug>(arg: T) {
| 18| 1| println!("used_only_from_bin_crate_generic_function with {:?}", arg);
| 19| 1|}
------------------
| used_crate::used_only_from_bin_crate_generic_function::<&alloc::vec::Vec<i32>>:
| used_crate::used_only_from_bin_crate_generic_function::<&str>:
| 17| 1|pub fn used_only_from_bin_crate_generic_function<T: Debug>(arg: T) {
| 18| 1| println!("used_only_from_bin_crate_generic_function with {:?}", arg);
| 19| 1|}

View File

@ -0,0 +1,10 @@
#!/usr/bin/env python
import sys
# Normalize file paths in output
for line in sys.stdin:
if line.startswith("..") and line.rstrip().endswith(".rs:"):
print(line.replace("\\", "/"), end='')
else:
print(line, end='')

View File

@ -0,0 +1,127 @@
<!DOCTYPE html>
<!--
Preview this file as rendered HTML from the github source at:
https://htmlpreview.github.io/?https://github.com/rust-lang/rust/blob/master/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.doctest/doctest.main.-------.InstrumentCoverage.0.html
For revisions in Pull Requests (PR):
* Replace "rust-lang" with the github PR author
* Replace "master" with the PR branch name
-->
<html>
<head>
<title>doctest.main - Coverage Spans</title>
<style>
.line {
counter-increment: line;
}
.line:before {
content: counter(line) ": ";
font-family: Menlo, Monaco, monospace;
font-style: italic;
width: 3.8em;
display: inline-block;
text-align: right;
filter: opacity(50%);
-webkit-user-select: none;
}
.code {
color: #dddddd;
background-color: #222222;
font-family: Menlo, Monaco, monospace;
line-height: 1.4em;
border-bottom: 2px solid #222222;
white-space: pre;
display: inline-block;
}
.odd {
background-color: #55bbff;
color: #223311;
}
.even {
background-color: #ee7756;
color: #551133;
}
.code {
--index: calc(var(--layer) - 1);
padding-top: calc(var(--index) * 0.15em);
filter:
hue-rotate(calc(var(--index) * 25deg))
saturate(calc(100% - (var(--index) * 2%)))
brightness(calc(100% - (var(--index) * 1.5%)));
}
.annotation {
color: #4444ff;
font-family: monospace;
font-style: italic;
display: none;
-webkit-user-select: none;
}
body:active .annotation {
/* requires holding mouse down anywhere on the page */
display: inline-block;
}
span:hover .annotation {
/* requires hover over a span ONLY on its first line */
display: inline-block;
}
</style>
</head>
<body>
<div class="code" style="counter-reset: line 59"><span class="line"><span><span class="code even" style="--layer: 1"><span class="annotation">@0⦊</span>fn main() <span class="annotation">⦉@0</span></span></span><span class="code" style="--layer: 0">{</span></span>
<span class="line"><span class="code" style="--layer: 0"> if </span><span><span class="code even" style="--layer: 1" title="61:8-61:12: @0[1]: _1 = const true
61:8-61:12: @0[2]: FakeRead(ForMatchedPlace, _1)"><span class="annotation">@0⦊</span>true<span class="annotation">⦉@0</span></span></span><span class="code" style="--layer: 0"> {</span></span>
<span class="line"><span class="code" style="--layer: 0"> </span><span><span class="code odd" style="--layer: 1" title="62:9-62:26: @5[0]: _2 = const ()"><span class="annotation">@5⦊</span></span></span><span class="code even" style="--layer: 2" title="62:9-62:26: @6[5]: _75 = const main::promoted[3]
62:9-62:26: @6[6]: _18 = &amp;(*_75)
62:9-62:26: @6[7]: _17 = &amp;(*_18)
62:9-62:26: @6[8]: _16 = move _17 as &amp;[&amp;str] (Pointer(Unsize))
62:9-62:26: @6[17]: _26 = &amp;(*_8)
62:9-62:26: @6[18]: _25 = &amp;_26
62:9-62:26: @6[21]: _28 = &amp;(*_9)
62:9-62:26: @6[22]: _27 = &amp;_28
62:9-62:26: @6[23]: _24 = (move _25, move _27)
62:9-62:26: @6[26]: FakeRead(ForMatchedPlace, _24)
62:9-62:26: @6[28]: _29 = (_24.0: &amp;&amp;i32)
62:9-62:26: @6[30]: _30 = (_24.1: &amp;&amp;i32)
62:9-62:26: @6[33]: _32 = &amp;(*_29)
62:9-62:26: @6[35]: _33 = &lt;&amp;i32 as Debug&gt;::fmt as for&lt;&#39;r, &#39;s, &#39;t0&gt; fn(&amp;&#39;r &amp;i32, &amp;&#39;s mut std::fmt::Formatter&lt;&#39;t0&gt;) -&gt; std::result::Result&lt;(), std::fmt::Error&gt; (Pointer(ReifyFnPointer))
62:9-62:26: @6.Call: _31 = ArgumentV1::new::&lt;&amp;i32&gt;(move _32, move _33) -&gt; [return: bb7, unwind: bb17]
62:9-62:26: @7[4]: _35 = &amp;(*_30)
62:9-62:26: @7[6]: _36 = &lt;&amp;i32 as Debug&gt;::fmt as for&lt;&#39;r, &#39;s, &#39;t0&gt; fn(&amp;&#39;r &amp;i32, &amp;&#39;s mut std::fmt::Formatter&lt;&#39;t0&gt;) -&gt; std::result::Result&lt;(), std::fmt::Error&gt; (Pointer(ReifyFnPointer))
62:9-62:26: @7.Call: _34 = ArgumentV1::new::&lt;&amp;i32&gt;(move _35, move _36) -&gt; [return: bb8, unwind: bb17]
62:9-62:26: @8[2]: _23 = [move _31, move _34]
62:9-62:26: @8[7]: _22 = &amp;_23
62:9-62:26: @8[8]: _21 = &amp;(*_22)
62:9-62:26: @8[9]: _20 = move _21 as &amp;[std::fmt::ArgumentV1] (Pointer(Unsize))
62:9-62:26: @8.Call: _15 = Arguments::new_v1(move _16, move _20) -&gt; [return: bb9, unwind: bb17]
62:9-62:26: @9.Call: core::panicking::panic_fmt(move _15) -&gt; bb17"><span class="annotation">@4,6,7,8,9⦊</span>assert_eq!(1, 1);<span class="annotation">⦉@4,6,7,8,9</span></span><span><span class="code odd" style="--layer: 1" title="62:9-62:26: @5[0]: _2 = const ()"><span class="annotation">⦉@5</span></span></span><span class="code" style="--layer: 0"></span></span>
<span class="line"><span class="code" style="--layer: 0"> } else {</span></span>
<span class="line"><span class="code" style="--layer: 0"> </span><span><span class="code even" style="--layer: 1" title="64:9-64:26: @11[0]: _37 = const ()"><span class="annotation">@11⦊</span></span></span><span class="code even" style="--layer: 2" title="64:9-64:26: @12[5]: _72 = const main::promoted[0]
64:9-64:26: @12[6]: _53 = &amp;(*_72)
64:9-64:26: @12[7]: _52 = &amp;(*_53)
64:9-64:26: @12[8]: _51 = move _52 as &amp;[&amp;str] (Pointer(Unsize))
64:9-64:26: @12[17]: _61 = &amp;(*_43)
64:9-64:26: @12[18]: _60 = &amp;_61
64:9-64:26: @12[21]: _63 = &amp;(*_44)
64:9-64:26: @12[22]: _62 = &amp;_63
64:9-64:26: @12[23]: _59 = (move _60, move _62)
64:9-64:26: @12[26]: FakeRead(ForMatchedPlace, _59)
64:9-64:26: @12[28]: _64 = (_59.0: &amp;&amp;i32)
64:9-64:26: @12[30]: _65 = (_59.1: &amp;&amp;i32)
64:9-64:26: @12[33]: _67 = &amp;(*_64)
64:9-64:26: @12[35]: _68 = &lt;&amp;i32 as Debug&gt;::fmt as for&lt;&#39;r, &#39;s, &#39;t0&gt; fn(&amp;&#39;r &amp;i32, &amp;&#39;s mut std::fmt::Formatter&lt;&#39;t0&gt;) -&gt; std::result::Result&lt;(), std::fmt::Error&gt; (Pointer(ReifyFnPointer))
64:9-64:26: @12.Call: _66 = ArgumentV1::new::&lt;&amp;i32&gt;(move _67, move _68) -&gt; [return: bb13, unwind: bb17]
64:9-64:26: @13[4]: _70 = &amp;(*_65)
64:9-64:26: @13[6]: _71 = &lt;&amp;i32 as Debug&gt;::fmt as for&lt;&#39;r, &#39;s, &#39;t0&gt; fn(&amp;&#39;r &amp;i32, &amp;&#39;s mut std::fmt::Formatter&lt;&#39;t0&gt;) -&gt; std::result::Result&lt;(), std::fmt::Error&gt; (Pointer(ReifyFnPointer))
64:9-64:26: @13.Call: _69 = ArgumentV1::new::&lt;&amp;i32&gt;(move _70, move _71) -&gt; [return: bb14, unwind: bb17]
64:9-64:26: @14[2]: _58 = [move _66, move _69]
64:9-64:26: @14[7]: _57 = &amp;_58
64:9-64:26: @14[8]: _56 = &amp;(*_57)
64:9-64:26: @14[9]: _55 = move _56 as &amp;[std::fmt::ArgumentV1] (Pointer(Unsize))
64:9-64:26: @14.Call: _50 = Arguments::new_v1(move _51, move _55) -&gt; [return: bb15, unwind: bb17]
64:9-64:26: @15.Call: core::panicking::panic_fmt(move _50) -&gt; bb17"><span class="annotation">@10,12,13,14,15⦊</span>assert_eq!(1, 2);<span class="annotation">⦉@10,12,13,14,15</span></span><span><span class="code even" style="--layer: 1" title="64:9-64:26: @11[0]: _37 = const ()"><span class="annotation">⦉@11</span></span></span><span class="code" style="--layer: 0"></span></span>
<span class="line"><span class="code" style="--layer: 0"> }</span></span>
<span class="line"><span class="code" style="--layer: 0">}</span><span><span class="code odd" style="--layer: 1" title="66:2-66:2: @16.Return: return"><span class="annotation">@16⦊</span><span class="annotation">⦉@16</span></span></span></span></div>
</body>
</html>

View File

@ -0,0 +1,173 @@
<!DOCTYPE html>
<!--
Preview this file as rendered HTML from the github source at:
https://htmlpreview.github.io/?https://github.com/rust-lang/rust/blob/master/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.doctest_crate/doctest_crate.fn_run_in_doctests.-------.InstrumentCoverage.0.html
For revisions in Pull Requests (PR):
* Replace "rust-lang" with the github PR author
* Replace "master" with the PR branch name
-->
<html>
<head>
<title>doctest_crate.fn_run_in_doctests - Coverage Spans</title>
<style>
.line {
counter-increment: line;
}
.line:before {
content: counter(line) ": ";
font-family: Menlo, Monaco, monospace;
font-style: italic;
width: 3.8em;
display: inline-block;
text-align: right;
filter: opacity(50%);
-webkit-user-select: none;
}
.code {
color: #dddddd;
background-color: #222222;
font-family: Menlo, Monaco, monospace;
line-height: 1.4em;
border-bottom: 2px solid #222222;
white-space: pre;
display: inline-block;
}
.odd {
background-color: #55bbff;
color: #223311;
}
.even {
background-color: #ee7756;
color: #551133;
}
.code {
--index: calc(var(--layer) - 1);
padding-top: calc(var(--index) * 0.15em);
filter:
hue-rotate(calc(var(--index) * 25deg))
saturate(calc(100% - (var(--index) * 2%)))
brightness(calc(100% - (var(--index) * 1.5%)));
}
.annotation {
color: #4444ff;
font-family: monospace;
font-style: italic;
display: none;
-webkit-user-select: none;
}
body:active .annotation {
/* requires holding mouse down anywhere on the page */
display: inline-block;
}
span:hover .annotation {
/* requires hover over a span ONLY on its first line */
display: inline-block;
}
</style>
</head>
<body>
<div class="code" style="counter-reset: line 1"><span class="line"><span><span class="code even" style="--layer: 1"><span class="annotation">@0⦊</span>pub fn fn_run_in_doctests(conditional: usize) <span class="annotation">⦉@0</span></span></span><span class="code" style="--layer: 0">{</span></span>
<span class="line"><span class="code" style="--layer: 0"> match </span><span><span class="code even" style="--layer: 1" title="3:11-3:22: @0[0]: FakeRead(ForMatchedPlace, _1)"><span class="annotation">@0⦊</span>conditional<span class="annotation">⦉@0</span></span></span><span class="code" style="--layer: 0"> {</span></span>
<span class="line"><span class="code" style="--layer: 0"> 1 =&gt; </span><span><span class="code odd" style="--layer: 1" title="4:14-4:30: @7[0]: _0 = const ()"><span class="annotation">@7⦊</span></span></span><span class="code even" style="--layer: 2" title="4:14-4:30: @8[5]: _138 = const fn_run_in_doctests::promoted[0]
4:14-4:30: @8[6]: _17 = &amp;(*_138)
4:14-4:30: @8[7]: _16 = &amp;(*_17)
4:14-4:30: @8[8]: _15 = move _16 as &amp;[&amp;str] (Pointer(Unsize))
4:14-4:30: @8[17]: _25 = &amp;(*_7)
4:14-4:30: @8[18]: _24 = &amp;_25
4:14-4:30: @8[21]: _27 = &amp;(*_8)
4:14-4:30: @8[22]: _26 = &amp;_27
4:14-4:30: @8[23]: _23 = (move _24, move _26)
4:14-4:30: @8[26]: FakeRead(ForMatchedPlace, _23)
4:14-4:30: @8[28]: _28 = (_23.0: &amp;&amp;i32)
4:14-4:30: @8[30]: _29 = (_23.1: &amp;&amp;i32)
4:14-4:30: @8[33]: _31 = &amp;(*_28)
4:14-4:30: @8[35]: _32 = &lt;&amp;i32 as Debug&gt;::fmt as for&lt;&#39;r, &#39;s, &#39;t0&gt; fn(&amp;&#39;r &amp;i32, &amp;&#39;s mut std::fmt::Formatter&lt;&#39;t0&gt;) -&gt; std::result::Result&lt;(), std::fmt::Error&gt; (Pointer(ReifyFnPointer))
4:14-4:30: @8.Call: _30 = ArgumentV1::new::&lt;&amp;i32&gt;(move _31, move _32) -&gt; [return: bb9, unwind: bb33]
4:14-4:30: @9[4]: _34 = &amp;(*_29)
4:14-4:30: @9[6]: _35 = &lt;&amp;i32 as Debug&gt;::fmt as for&lt;&#39;r, &#39;s, &#39;t0&gt; fn(&amp;&#39;r &amp;i32, &amp;&#39;s mut std::fmt::Formatter&lt;&#39;t0&gt;) -&gt; std::result::Result&lt;(), std::fmt::Error&gt; (Pointer(ReifyFnPointer))
4:14-4:30: @9.Call: _33 = ArgumentV1::new::&lt;&amp;i32&gt;(move _34, move _35) -&gt; [return: bb10, unwind: bb33]
4:14-4:30: @10[2]: _22 = [move _30, move _33]
4:14-4:30: @10[7]: _21 = &amp;_22
4:14-4:30: @10[8]: _20 = &amp;(*_21)
4:14-4:30: @10[9]: _19 = move _20 as &amp;[std::fmt::ArgumentV1] (Pointer(Unsize))
4:14-4:30: @10.Call: _14 = Arguments::new_v1(move _15, move _19) -&gt; [return: bb11, unwind: bb33]
4:14-4:30: @11.Call: core::panicking::panic_fmt(move _14) -&gt; bb33"><span class="annotation">@6,8,9,10,11⦊</span>assert_eq!(1, 1)<span class="annotation">⦉@6,8,9,10,11</span></span><span><span class="code odd" style="--layer: 1" title="4:14-4:30: @7[0]: _0 = const ()"><span class="annotation">⦉@7</span></span></span><span class="code" style="--layer: 0">, // this is run,</span></span>
<span class="line"><span class="code" style="--layer: 0"> 2 =&gt; </span><span><span class="code even" style="--layer: 1" title="5:14-5:30: @14[0]: _0 = const ()"><span class="annotation">@14⦊</span></span></span><span class="code even" style="--layer: 2" title="5:14-5:30: @15[5]: _141 = const fn_run_in_doctests::promoted[3]
5:14-5:30: @15[6]: _51 = &amp;(*_141)
5:14-5:30: @15[7]: _50 = &amp;(*_51)
5:14-5:30: @15[8]: _49 = move _50 as &amp;[&amp;str] (Pointer(Unsize))
5:14-5:30: @15[17]: _59 = &amp;(*_41)
5:14-5:30: @15[18]: _58 = &amp;_59
5:14-5:30: @15[21]: _61 = &amp;(*_42)
5:14-5:30: @15[22]: _60 = &amp;_61
5:14-5:30: @15[23]: _57 = (move _58, move _60)
5:14-5:30: @15[26]: FakeRead(ForMatchedPlace, _57)
5:14-5:30: @15[28]: _62 = (_57.0: &amp;&amp;i32)
5:14-5:30: @15[30]: _63 = (_57.1: &amp;&amp;i32)
5:14-5:30: @15[33]: _65 = &amp;(*_62)
5:14-5:30: @15[35]: _66 = &lt;&amp;i32 as Debug&gt;::fmt as for&lt;&#39;r, &#39;s, &#39;t0&gt; fn(&amp;&#39;r &amp;i32, &amp;&#39;s mut std::fmt::Formatter&lt;&#39;t0&gt;) -&gt; std::result::Result&lt;(), std::fmt::Error&gt; (Pointer(ReifyFnPointer))
5:14-5:30: @15.Call: _64 = ArgumentV1::new::&lt;&amp;i32&gt;(move _65, move _66) -&gt; [return: bb16, unwind: bb33]
5:14-5:30: @16[4]: _68 = &amp;(*_63)
5:14-5:30: @16[6]: _69 = &lt;&amp;i32 as Debug&gt;::fmt as for&lt;&#39;r, &#39;s, &#39;t0&gt; fn(&amp;&#39;r &amp;i32, &amp;&#39;s mut std::fmt::Formatter&lt;&#39;t0&gt;) -&gt; std::result::Result&lt;(), std::fmt::Error&gt; (Pointer(ReifyFnPointer))
5:14-5:30: @16.Call: _67 = ArgumentV1::new::&lt;&amp;i32&gt;(move _68, move _69) -&gt; [return: bb17, unwind: bb33]
5:14-5:30: @17[2]: _56 = [move _64, move _67]
5:14-5:30: @17[7]: _55 = &amp;_56
5:14-5:30: @17[8]: _54 = &amp;(*_55)
5:14-5:30: @17[9]: _53 = move _54 as &amp;[std::fmt::ArgumentV1] (Pointer(Unsize))
5:14-5:30: @17.Call: _48 = Arguments::new_v1(move _49, move _53) -&gt; [return: bb18, unwind: bb33]
5:14-5:30: @18.Call: core::panicking::panic_fmt(move _48) -&gt; bb33"><span class="annotation">@13,15,16,17,18⦊</span>assert_eq!(1, 1)<span class="annotation">⦉@13,15,16,17,18</span></span><span><span class="code even" style="--layer: 1" title="5:14-5:30: @14[0]: _0 = const ()"><span class="annotation">⦉@14</span></span></span><span class="code" style="--layer: 0">, // this,</span></span>
<span class="line"><span class="code" style="--layer: 0"> 3 =&gt; </span><span><span class="code odd" style="--layer: 1" title="6:14-6:30: @21[0]: _0 = const ()"><span class="annotation">@21⦊</span></span></span><span class="code even" style="--layer: 2" title="6:14-6:30: @22[5]: _144 = const fn_run_in_doctests::promoted[6]
6:14-6:30: @22[6]: _85 = &amp;(*_144)
6:14-6:30: @22[7]: _84 = &amp;(*_85)
6:14-6:30: @22[8]: _83 = move _84 as &amp;[&amp;str] (Pointer(Unsize))
6:14-6:30: @22[17]: _93 = &amp;(*_75)
6:14-6:30: @22[18]: _92 = &amp;_93
6:14-6:30: @22[21]: _95 = &amp;(*_76)
6:14-6:30: @22[22]: _94 = &amp;_95
6:14-6:30: @22[23]: _91 = (move _92, move _94)
6:14-6:30: @22[26]: FakeRead(ForMatchedPlace, _91)
6:14-6:30: @22[28]: _96 = (_91.0: &amp;&amp;i32)
6:14-6:30: @22[30]: _97 = (_91.1: &amp;&amp;i32)
6:14-6:30: @22[33]: _99 = &amp;(*_96)
6:14-6:30: @22[35]: _100 = &lt;&amp;i32 as Debug&gt;::fmt as for&lt;&#39;r, &#39;s, &#39;t0&gt; fn(&amp;&#39;r &amp;i32, &amp;&#39;s mut std::fmt::Formatter&lt;&#39;t0&gt;) -&gt; std::result::Result&lt;(), std::fmt::Error&gt; (Pointer(ReifyFnPointer))
6:14-6:30: @22.Call: _98 = ArgumentV1::new::&lt;&amp;i32&gt;(move _99, move _100) -&gt; [return: bb23, unwind: bb33]
6:14-6:30: @23[4]: _102 = &amp;(*_97)
6:14-6:30: @23[6]: _103 = &lt;&amp;i32 as Debug&gt;::fmt as for&lt;&#39;r, &#39;s, &#39;t0&gt; fn(&amp;&#39;r &amp;i32, &amp;&#39;s mut std::fmt::Formatter&lt;&#39;t0&gt;) -&gt; std::result::Result&lt;(), std::fmt::Error&gt; (Pointer(ReifyFnPointer))
6:14-6:30: @23.Call: _101 = ArgumentV1::new::&lt;&amp;i32&gt;(move _102, move _103) -&gt; [return: bb24, unwind: bb33]
6:14-6:30: @24[2]: _90 = [move _98, move _101]
6:14-6:30: @24[7]: _89 = &amp;_90
6:14-6:30: @24[8]: _88 = &amp;(*_89)
6:14-6:30: @24[9]: _87 = move _88 as &amp;[std::fmt::ArgumentV1] (Pointer(Unsize))
6:14-6:30: @24.Call: _82 = Arguments::new_v1(move _83, move _87) -&gt; [return: bb25, unwind: bb33]
6:14-6:30: @25.Call: core::panicking::panic_fmt(move _82) -&gt; bb33"><span class="annotation">@20,22,23,24,25⦊</span>assert_eq!(1, 1)<span class="annotation">⦉@20,22,23,24,25</span></span><span><span class="code odd" style="--layer: 1" title="6:14-6:30: @21[0]: _0 = const ()"><span class="annotation">⦉@21</span></span></span><span class="code" style="--layer: 0">, // and this too</span></span>
<span class="line"><span class="code" style="--layer: 0"> _ =&gt; </span><span><span class="code even" style="--layer: 1" title="7:14-7:30: @27[0]: _0 = const ()"><span class="annotation">@27⦊</span></span></span><span class="code even" style="--layer: 2" title="7:14-7:30: @28[5]: _147 = const fn_run_in_doctests::promoted[9]
7:14-7:30: @28[6]: _119 = &amp;(*_147)
7:14-7:30: @28[7]: _118 = &amp;(*_119)
7:14-7:30: @28[8]: _117 = move _118 as &amp;[&amp;str] (Pointer(Unsize))
7:14-7:30: @28[17]: _127 = &amp;(*_109)
7:14-7:30: @28[18]: _126 = &amp;_127
7:14-7:30: @28[21]: _129 = &amp;(*_110)
7:14-7:30: @28[22]: _128 = &amp;_129
7:14-7:30: @28[23]: _125 = (move _126, move _128)
7:14-7:30: @28[26]: FakeRead(ForMatchedPlace, _125)
7:14-7:30: @28[28]: _130 = (_125.0: &amp;&amp;i32)
7:14-7:30: @28[30]: _131 = (_125.1: &amp;&amp;i32)
7:14-7:30: @28[33]: _133 = &amp;(*_130)
7:14-7:30: @28[35]: _134 = &lt;&amp;i32 as Debug&gt;::fmt as for&lt;&#39;r, &#39;s, &#39;t0&gt; fn(&amp;&#39;r &amp;i32, &amp;&#39;s mut std::fmt::Formatter&lt;&#39;t0&gt;) -&gt; std::result::Result&lt;(), std::fmt::Error&gt; (Pointer(ReifyFnPointer))
7:14-7:30: @28.Call: _132 = ArgumentV1::new::&lt;&amp;i32&gt;(move _133, move _134) -&gt; [return: bb29, unwind: bb33]
7:14-7:30: @29[4]: _136 = &amp;(*_131)
7:14-7:30: @29[6]: _137 = &lt;&amp;i32 as Debug&gt;::fmt as for&lt;&#39;r, &#39;s, &#39;t0&gt; fn(&amp;&#39;r &amp;i32, &amp;&#39;s mut std::fmt::Formatter&lt;&#39;t0&gt;) -&gt; std::result::Result&lt;(), std::fmt::Error&gt; (Pointer(ReifyFnPointer))
7:14-7:30: @29.Call: _135 = ArgumentV1::new::&lt;&amp;i32&gt;(move _136, move _137) -&gt; [return: bb30, unwind: bb33]
7:14-7:30: @30[2]: _124 = [move _132, move _135]
7:14-7:30: @30[7]: _123 = &amp;_124
7:14-7:30: @30[8]: _122 = &amp;(*_123)
7:14-7:30: @30[9]: _121 = move _122 as &amp;[std::fmt::ArgumentV1] (Pointer(Unsize))
7:14-7:30: @30.Call: _116 = Arguments::new_v1(move _117, move _121) -&gt; [return: bb31, unwind: bb33]
7:14-7:30: @31.Call: core::panicking::panic_fmt(move _116) -&gt; bb33"><span class="annotation">@26,28,29,30,31⦊</span>assert_eq!(1, 2)<span class="annotation">⦉@26,28,29,30,31</span></span><span><span class="code even" style="--layer: 1" title="7:14-7:30: @27[0]: _0 = const ()"><span class="annotation">⦉@27</span></span></span><span class="code" style="--layer: 0">, // however this is not</span></span>
<span class="line"><span class="code" style="--layer: 0"> }</span></span>
<span class="line"><span class="code" style="--layer: 0">}</span><span><span class="code odd" style="--layer: 1" title="9:2-9:2: @32.Return: return"><span class="annotation">@32⦊</span><span class="annotation">⦉@32</span></span></span></span></div>
</body>
</html>

View File

@ -1,3 +1,3 @@
# Directory "instrument-coverage" supports the tests at prefix ../instrument-coverage-*
# Directory "coverage" supports the tests at prefix ../coverage-*
# Use ./x.py [options] test src/test/run-make-fulldeps/instrument-coverage to run all related tests.
# Use ./x.py [options] test src/test/run-make-fulldeps/coverage to run all related tests.

View File

@ -1,7 +1,7 @@
# Common Makefile include for Rust `run-make-fulldeps/instrument-coverage-* tests. Include this
# Common Makefile include for Rust `run-make-fulldeps/coverage-* tests. Include this
# file with the line:
#
# -include ../instrument-coverage/coverage_tools.mk
# -include ../coverage/coverage_tools.mk
-include ../tools.mk

View File

@ -0,0 +1,66 @@
//! This test ensures that code from doctests is properly re-mapped.
//! See <https://github.com/rust-lang/rust/issues/79417> for more info.
//!
//! Just some random code:
//! ```
//! if true {
//! // this is executed!
//! assert_eq!(1, 1);
//! } else {
//! // this is not!
//! assert_eq!(1, 2);
//! }
//! ```
//!
//! doctest testing external code:
//! ```
//! extern crate doctest_crate;
//! doctest_crate::fn_run_in_doctests(1);
//! ```
//!
//! doctest returning a result:
//! ```
//! #[derive(Debug)]
//! struct SomeError;
//! let mut res = Err(SomeError);
//! if res.is_ok() {
//! res?;
//! } else {
//! res = Ok(0);
//! }
//! // need to be explicit because rustdoc cant infer the return type
//! Ok::<(), SomeError>(())
//! ```
//!
//! doctest with custom main:
//! ```
//! #[derive(Debug)]
//! struct SomeError;
//!
//! extern crate doctest_crate;
//!
//! fn doctest_main() -> Result<(), SomeError> {
//! doctest_crate::fn_run_in_doctests(2);
//! Ok(())
//! }
//!
//! // this `main` is not shown as covered, as it clashes with all the other
//! // `main` functions that were automatically generated for doctests
//! fn main() -> Result<(), SomeError> {
//! doctest_main()
//! }
//! ```
/// doctest attached to fn testing external code:
/// ```
/// extern crate doctest_crate;
/// doctest_crate::fn_run_in_doctests(3);
/// ```
///
fn main() {
if true {
assert_eq!(1, 1);
} else {
assert_eq!(1, 2);
}
}

View File

@ -0,0 +1,9 @@
/// A function run only from within doctests
pub fn fn_run_in_doctests(conditional: usize) {
match conditional {
1 => assert_eq!(1, 1), // this is run,
2 => assert_eq!(1, 1), // this,
3 => assert_eq!(1, 1), // and this too
_ => assert_eq!(1, 2), // however this is not
}
}