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.
This commit is contained in:
bors 2020-12-25 02:37:08 +00:00
commit cae1f4ddf2
15 changed files with 599 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,41 @@ 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 inner_attr = if test_id.is_some() { "#[allow(non_snake_case)] " } else { "" };
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_attr, inner_fn_name
),
format!("\n}}; {}().unwrap() }}", inner_fn_name),
)
} else if test_id.is_some() {
(
format!("fn main() {{ {}fn {}() {{\n", inner_attr, 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 +776,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 +840,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() { #[allow(non_snake_case)] 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,12 @@
#!/usr/bin/env python
from __future__ import print_function
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
}
}