};
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.
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)),
);
}
}
/// 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,
} 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,
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: _ }) => {
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");
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();
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);
_ => 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");
edition,
outdir,
path,
+ &test_id,
);
if let Err(err) = res {
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));
}
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));
}
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));
}
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));
}
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));
}
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));
}
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));
}
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.
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));
}
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));
}
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));
}
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));
}
//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));
}
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));
}
}"
.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;
}"
.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));
}
}"
.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));
+}
.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(") { "&version=nightly" } else { "" };
let edition_string = format!("&edition={}", edition);
# 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=$$?; \
) \
)
+ # 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`.
--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 \
--- /dev/null
+../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|}
+
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|}
--- /dev/null
+#!/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='')
--- /dev/null
+<!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 = &(*_75)
+62:9-62:26: @6[7]: _17 = &(*_18)
+62:9-62:26: @6[8]: _16 = move _17 as &[&str] (Pointer(Unsize))
+62:9-62:26: @6[17]: _26 = &(*_8)
+62:9-62:26: @6[18]: _25 = &_26
+62:9-62:26: @6[21]: _28 = &(*_9)
+62:9-62:26: @6[22]: _27 = &_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: &&i32)
+62:9-62:26: @6[30]: _30 = (_24.1: &&i32)
+62:9-62:26: @6[33]: _32 = &(*_29)
+62:9-62:26: @6[35]: _33 = <&i32 as Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer))
+62:9-62:26: @6.Call: _31 = ArgumentV1::new::<&i32>(move _32, move _33) -> [return: bb7, unwind: bb17]
+62:9-62:26: @7[4]: _35 = &(*_30)
+62:9-62:26: @7[6]: _36 = <&i32 as Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer))
+62:9-62:26: @7.Call: _34 = ArgumentV1::new::<&i32>(move _35, move _36) -> [return: bb8, unwind: bb17]
+62:9-62:26: @8[2]: _23 = [move _31, move _34]
+62:9-62:26: @8[7]: _22 = &_23
+62:9-62:26: @8[8]: _21 = &(*_22)
+62:9-62:26: @8[9]: _20 = move _21 as &[std::fmt::ArgumentV1] (Pointer(Unsize))
+62:9-62:26: @8.Call: _15 = Arguments::new_v1(move _16, move _20) -> [return: bb9, unwind: bb17]
+62:9-62:26: @9.Call: core::panicking::panic_fmt(move _15) -> 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 = &(*_72)
+64:9-64:26: @12[7]: _52 = &(*_53)
+64:9-64:26: @12[8]: _51 = move _52 as &[&str] (Pointer(Unsize))
+64:9-64:26: @12[17]: _61 = &(*_43)
+64:9-64:26: @12[18]: _60 = &_61
+64:9-64:26: @12[21]: _63 = &(*_44)
+64:9-64:26: @12[22]: _62 = &_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: &&i32)
+64:9-64:26: @12[30]: _65 = (_59.1: &&i32)
+64:9-64:26: @12[33]: _67 = &(*_64)
+64:9-64:26: @12[35]: _68 = <&i32 as Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer))
+64:9-64:26: @12.Call: _66 = ArgumentV1::new::<&i32>(move _67, move _68) -> [return: bb13, unwind: bb17]
+64:9-64:26: @13[4]: _70 = &(*_65)
+64:9-64:26: @13[6]: _71 = <&i32 as Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer))
+64:9-64:26: @13.Call: _69 = ArgumentV1::new::<&i32>(move _70, move _71) -> [return: bb14, unwind: bb17]
+64:9-64:26: @14[2]: _58 = [move _66, move _69]
+64:9-64:26: @14[7]: _57 = &_58
+64:9-64:26: @14[8]: _56 = &(*_57)
+64:9-64:26: @14[9]: _55 = move _56 as &[std::fmt::ArgumentV1] (Pointer(Unsize))
+64:9-64:26: @14.Call: _50 = Arguments::new_v1(move _51, move _55) -> [return: bb15, unwind: bb17]
+64:9-64:26: @15.Call: core::panicking::panic_fmt(move _50) -> 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>
--- /dev/null
+<!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 => </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 = &(*_138)
+4:14-4:30: @8[7]: _16 = &(*_17)
+4:14-4:30: @8[8]: _15 = move _16 as &[&str] (Pointer(Unsize))
+4:14-4:30: @8[17]: _25 = &(*_7)
+4:14-4:30: @8[18]: _24 = &_25
+4:14-4:30: @8[21]: _27 = &(*_8)
+4:14-4:30: @8[22]: _26 = &_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: &&i32)
+4:14-4:30: @8[30]: _29 = (_23.1: &&i32)
+4:14-4:30: @8[33]: _31 = &(*_28)
+4:14-4:30: @8[35]: _32 = <&i32 as Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer))
+4:14-4:30: @8.Call: _30 = ArgumentV1::new::<&i32>(move _31, move _32) -> [return: bb9, unwind: bb33]
+4:14-4:30: @9[4]: _34 = &(*_29)
+4:14-4:30: @9[6]: _35 = <&i32 as Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer))
+4:14-4:30: @9.Call: _33 = ArgumentV1::new::<&i32>(move _34, move _35) -> [return: bb10, unwind: bb33]
+4:14-4:30: @10[2]: _22 = [move _30, move _33]
+4:14-4:30: @10[7]: _21 = &_22
+4:14-4:30: @10[8]: _20 = &(*_21)
+4:14-4:30: @10[9]: _19 = move _20 as &[std::fmt::ArgumentV1] (Pointer(Unsize))
+4:14-4:30: @10.Call: _14 = Arguments::new_v1(move _15, move _19) -> [return: bb11, unwind: bb33]
+4:14-4:30: @11.Call: core::panicking::panic_fmt(move _14) -> 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 => </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 = &(*_141)
+5:14-5:30: @15[7]: _50 = &(*_51)
+5:14-5:30: @15[8]: _49 = move _50 as &[&str] (Pointer(Unsize))
+5:14-5:30: @15[17]: _59 = &(*_41)
+5:14-5:30: @15[18]: _58 = &_59
+5:14-5:30: @15[21]: _61 = &(*_42)
+5:14-5:30: @15[22]: _60 = &_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: &&i32)
+5:14-5:30: @15[30]: _63 = (_57.1: &&i32)
+5:14-5:30: @15[33]: _65 = &(*_62)
+5:14-5:30: @15[35]: _66 = <&i32 as Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer))
+5:14-5:30: @15.Call: _64 = ArgumentV1::new::<&i32>(move _65, move _66) -> [return: bb16, unwind: bb33]
+5:14-5:30: @16[4]: _68 = &(*_63)
+5:14-5:30: @16[6]: _69 = <&i32 as Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer))
+5:14-5:30: @16.Call: _67 = ArgumentV1::new::<&i32>(move _68, move _69) -> [return: bb17, unwind: bb33]
+5:14-5:30: @17[2]: _56 = [move _64, move _67]
+5:14-5:30: @17[7]: _55 = &_56
+5:14-5:30: @17[8]: _54 = &(*_55)
+5:14-5:30: @17[9]: _53 = move _54 as &[std::fmt::ArgumentV1] (Pointer(Unsize))
+5:14-5:30: @17.Call: _48 = Arguments::new_v1(move _49, move _53) -> [return: bb18, unwind: bb33]
+5:14-5:30: @18.Call: core::panicking::panic_fmt(move _48) -> 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 => </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 = &(*_144)
+6:14-6:30: @22[7]: _84 = &(*_85)
+6:14-6:30: @22[8]: _83 = move _84 as &[&str] (Pointer(Unsize))
+6:14-6:30: @22[17]: _93 = &(*_75)
+6:14-6:30: @22[18]: _92 = &_93
+6:14-6:30: @22[21]: _95 = &(*_76)
+6:14-6:30: @22[22]: _94 = &_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: &&i32)
+6:14-6:30: @22[30]: _97 = (_91.1: &&i32)
+6:14-6:30: @22[33]: _99 = &(*_96)
+6:14-6:30: @22[35]: _100 = <&i32 as Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer))
+6:14-6:30: @22.Call: _98 = ArgumentV1::new::<&i32>(move _99, move _100) -> [return: bb23, unwind: bb33]
+6:14-6:30: @23[4]: _102 = &(*_97)
+6:14-6:30: @23[6]: _103 = <&i32 as Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer))
+6:14-6:30: @23.Call: _101 = ArgumentV1::new::<&i32>(move _102, move _103) -> [return: bb24, unwind: bb33]
+6:14-6:30: @24[2]: _90 = [move _98, move _101]
+6:14-6:30: @24[7]: _89 = &_90
+6:14-6:30: @24[8]: _88 = &(*_89)
+6:14-6:30: @24[9]: _87 = move _88 as &[std::fmt::ArgumentV1] (Pointer(Unsize))
+6:14-6:30: @24.Call: _82 = Arguments::new_v1(move _83, move _87) -> [return: bb25, unwind: bb33]
+6:14-6:30: @25.Call: core::panicking::panic_fmt(move _82) -> 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"> _ => </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 = &(*_147)
+7:14-7:30: @28[7]: _118 = &(*_119)
+7:14-7:30: @28[8]: _117 = move _118 as &[&str] (Pointer(Unsize))
+7:14-7:30: @28[17]: _127 = &(*_109)
+7:14-7:30: @28[18]: _126 = &_127
+7:14-7:30: @28[21]: _129 = &(*_110)
+7:14-7:30: @28[22]: _128 = &_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: &&i32)
+7:14-7:30: @28[30]: _131 = (_125.1: &&i32)
+7:14-7:30: @28[33]: _133 = &(*_130)
+7:14-7:30: @28[35]: _134 = <&i32 as Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer))
+7:14-7:30: @28.Call: _132 = ArgumentV1::new::<&i32>(move _133, move _134) -> [return: bb29, unwind: bb33]
+7:14-7:30: @29[4]: _136 = &(*_131)
+7:14-7:30: @29[6]: _137 = <&i32 as Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer))
+7:14-7:30: @29.Call: _135 = ArgumentV1::new::<&i32>(move _136, move _137) -> [return: bb30, unwind: bb33]
+7:14-7:30: @30[2]: _124 = [move _132, move _135]
+7:14-7:30: @30[7]: _123 = &_124
+7:14-7:30: @30[8]: _122 = &(*_123)
+7:14-7:30: @30[9]: _121 = move _122 as &[std::fmt::ArgumentV1] (Pointer(Unsize))
+7:14-7:30: @30.Call: _116 = Arguments::new_v1(move _117, move _121) -> [return: bb31, unwind: bb33]
+7:14-7:30: @31.Call: core::panicking::panic_fmt(move _116) -> 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>
-# 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.
-# 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
--- /dev/null
+//! 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);
+ }
+}
--- /dev/null
+/// 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
+ }
+}