Rollup merge of #119566 - Zalathar:remove-spanview, r=Swatinem,Nilstrieb
Remove `-Zdump-mir-spanview` The `-Zdump-mir-spanview` flag was added back in #76074, as a development/debugging aid for the initial work on what would eventually become `-Cinstrument-coverage`. It causes the compiler to emit an HTML file containing a function's source code, with various spans highlighted based on the contents of MIR. When the suggestion was made to [triage and remove unnecessary `-Z` flags (Zulip)](https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/.60-Z.60.20option.20triage), I noted that this flag could potentially be worth removing, but I wanted to keep it around to see whether I found it useful for my own coverage work. But when I actually tried to use it, I ran into various issues (e.g. it crashes on `tests/coverage/closure.rs`). If I can't trust it to work properly without a full overhaul, then instead of diving down a rabbit hole of trying to fix arcane span-handling bugs, it seems better to just remove this obscure old code entirely. --- ````@rustbot```` label +A-code-coverage
This commit is contained in:
commit
e74a0cdfed
@ -6,8 +6,8 @@
|
||||
build_configuration, build_session_options, rustc_optgroups, BranchProtection, CFGuard, Cfg,
|
||||
DebugInfo, DumpMonoStatsFormat, ErrorOutputType, ExternEntry, ExternLocation, Externs,
|
||||
FunctionReturn, InliningThreshold, Input, InstrumentCoverage, InstrumentXRay,
|
||||
LinkSelfContained, LinkerPluginLto, LocationDetail, LtoCli, MirSpanview, NextSolverConfig,
|
||||
OomStrategy, Options, OutFileName, OutputType, OutputTypes, PAuthKey, PacRet, Passes, Polonius,
|
||||
LinkSelfContained, LinkerPluginLto, LocationDetail, LtoCli, NextSolverConfig, OomStrategy,
|
||||
Options, OutFileName, OutputType, OutputTypes, PAuthKey, PacRet, Passes, Polonius,
|
||||
ProcMacroExecutionStrategy, Strip, SwitchWithOptPath, SymbolManglingVersion, WasiExecModel,
|
||||
};
|
||||
use rustc_session::lint::Level;
|
||||
@ -666,7 +666,6 @@ macro_rules! untracked {
|
||||
untracked!(dump_mir_dir, String::from("abc"));
|
||||
untracked!(dump_mir_exclude_pass_number, true);
|
||||
untracked!(dump_mir_graphviz, true);
|
||||
untracked!(dump_mir_spanview, Some(MirSpanview::Statement));
|
||||
untracked!(dump_mono_stats, SwitchWithOptPath::Enabled(Some("mono-items-dir/".into())));
|
||||
untracked!(dump_mono_stats_format, DumpMonoStatsFormat::Json);
|
||||
untracked!(dylib_lto, true);
|
||||
|
@ -55,7 +55,6 @@
|
||||
pub mod patch;
|
||||
pub mod pretty;
|
||||
mod query;
|
||||
pub mod spanview;
|
||||
mod statement;
|
||||
mod syntax;
|
||||
pub mod tcx;
|
||||
|
@ -5,7 +5,6 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use super::graphviz::write_mir_fn_graphviz;
|
||||
use super::spanview::write_mir_fn_spanview;
|
||||
use rustc_ast::InlineAsmTemplatePiece;
|
||||
use rustc_middle::mir::interpret::{
|
||||
alloc_range, read_target_uint, AllocBytes, AllocId, Allocation, GlobalAlloc, Pointer,
|
||||
@ -141,16 +140,6 @@ fn dump_matched_mir_node<'tcx, F>(
|
||||
write_mir_fn_graphviz(tcx, body, false, &mut file)?;
|
||||
};
|
||||
}
|
||||
|
||||
if let Some(spanview) = tcx.sess.opts.unstable_opts.dump_mir_spanview {
|
||||
let _: io::Result<()> = try {
|
||||
let file_basename = dump_file_basename(tcx, pass_num, pass_name, disambiguator, body);
|
||||
let mut file = create_dump_file_with_basename(tcx, &file_basename, "html")?;
|
||||
if body.source.def_id().is_local() {
|
||||
write_mir_fn_spanview(tcx, body, spanview, &file_basename, &mut file)?;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the file basename portion (without extension) of a filename path
|
||||
|
@ -1,642 +0,0 @@
|
||||
use rustc_middle::hir;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_session::config::MirSpanview;
|
||||
use rustc_span::{BytePos, Pos};
|
||||
|
||||
use std::cmp;
|
||||
use std::io::{self, Write};
|
||||
|
||||
pub const TOOLTIP_INDENT: &str = " ";
|
||||
|
||||
const CARET: char = '\u{2038}'; // Unicode `CARET`
|
||||
const ANNOTATION_LEFT_BRACKET: char = '\u{298a}'; // Unicode `Z NOTATION RIGHT BINDING BRACKET`
|
||||
const ANNOTATION_RIGHT_BRACKET: char = '\u{2989}'; // Unicode `Z NOTATION LEFT BINDING BRACKET`
|
||||
const NEW_LINE_SPAN: &str = "</span>\n<span class=\"line\">";
|
||||
const HEADER: &str = r#"<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">"#;
|
||||
const START_BODY: &str = r#"</head>
|
||||
<body>"#;
|
||||
const FOOTER: &str = r#"</body>
|
||||
</html>"#;
|
||||
|
||||
const STYLE_SECTION: &str = r#"<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>"#;
|
||||
|
||||
/// Metadata to highlight the span of a MIR BasicBlock, Statement, or Terminator.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SpanViewable {
|
||||
pub bb: BasicBlock,
|
||||
pub span: Span,
|
||||
pub id: String,
|
||||
pub tooltip: String,
|
||||
}
|
||||
|
||||
/// Write a spanview HTML+CSS file to analyze MIR element spans.
|
||||
pub fn write_mir_fn_spanview<'tcx, W>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
spanview: MirSpanview,
|
||||
title: &str,
|
||||
w: &mut W,
|
||||
) -> io::Result<()>
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
let def_id = body.source.def_id();
|
||||
let hir_body = hir_body(tcx, def_id);
|
||||
if hir_body.is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
let body_span = hir_body.unwrap().value.span;
|
||||
let mut span_viewables = Vec::new();
|
||||
for (bb, data) in body.basic_blocks.iter_enumerated() {
|
||||
match spanview {
|
||||
MirSpanview::Statement => {
|
||||
for (i, statement) in data.statements.iter().enumerate() {
|
||||
if let Some(span_viewable) =
|
||||
statement_span_viewable(tcx, body_span, bb, i, statement)
|
||||
{
|
||||
span_viewables.push(span_viewable);
|
||||
}
|
||||
}
|
||||
if let Some(span_viewable) = terminator_span_viewable(tcx, body_span, bb, data) {
|
||||
span_viewables.push(span_viewable);
|
||||
}
|
||||
}
|
||||
MirSpanview::Terminator => {
|
||||
if let Some(span_viewable) = terminator_span_viewable(tcx, body_span, bb, data) {
|
||||
span_viewables.push(span_viewable);
|
||||
}
|
||||
}
|
||||
MirSpanview::Block => {
|
||||
if let Some(span_viewable) = block_span_viewable(tcx, body_span, bb, data) {
|
||||
span_viewables.push(span_viewable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
write_document(tcx, fn_span(tcx, def_id), span_viewables, title, w)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Generate a spanview HTML+CSS document for the given local function `def_id`, and a pre-generated
|
||||
/// list `SpanViewable`s.
|
||||
pub fn write_document<'tcx, W>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
spanview_span: Span,
|
||||
mut span_viewables: Vec<SpanViewable>,
|
||||
title: &str,
|
||||
w: &mut W,
|
||||
) -> io::Result<()>
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
let mut from_pos = spanview_span.lo();
|
||||
let end_pos = spanview_span.hi();
|
||||
let source_map = tcx.sess.source_map();
|
||||
let start = source_map.lookup_char_pos(from_pos);
|
||||
let indent_to_initial_start_col = " ".repeat(start.col.to_usize());
|
||||
debug!(
|
||||
"spanview_span={:?}; source is:\n{}{}",
|
||||
spanview_span,
|
||||
indent_to_initial_start_col,
|
||||
source_map.span_to_snippet(spanview_span).expect("function should have printable source")
|
||||
);
|
||||
writeln!(w, "{HEADER}")?;
|
||||
writeln!(w, "<title>{title}</title>")?;
|
||||
writeln!(w, "{STYLE_SECTION}")?;
|
||||
writeln!(w, "{START_BODY}")?;
|
||||
write!(
|
||||
w,
|
||||
r#"<div class="code" style="counter-reset: line {}"><span class="line">{}"#,
|
||||
start.line - 1,
|
||||
indent_to_initial_start_col,
|
||||
)?;
|
||||
span_viewables.sort_unstable_by(|a, b| {
|
||||
let a = a.span;
|
||||
let b = b.span;
|
||||
if a.lo() == b.lo() {
|
||||
// Sort hi() in reverse order so shorter spans are attempted after longer spans.
|
||||
// This should give shorter spans a higher "layer", so they are not covered by
|
||||
// the longer spans.
|
||||
b.hi().partial_cmp(&a.hi())
|
||||
} else {
|
||||
a.lo().partial_cmp(&b.lo())
|
||||
}
|
||||
.unwrap()
|
||||
});
|
||||
let mut ordered_viewables = &span_viewables[..];
|
||||
const LOWEST_VIEWABLE_LAYER: usize = 1;
|
||||
let mut alt = false;
|
||||
while ordered_viewables.len() > 0 {
|
||||
debug!(
|
||||
"calling write_next_viewable with from_pos={}, end_pos={}, and viewables len={}",
|
||||
from_pos.to_usize(),
|
||||
end_pos.to_usize(),
|
||||
ordered_viewables.len()
|
||||
);
|
||||
let curr_id = &ordered_viewables[0].id;
|
||||
let (next_from_pos, next_ordered_viewables) = write_next_viewable_with_overlaps(
|
||||
tcx,
|
||||
from_pos,
|
||||
end_pos,
|
||||
ordered_viewables,
|
||||
alt,
|
||||
LOWEST_VIEWABLE_LAYER,
|
||||
w,
|
||||
)?;
|
||||
debug!(
|
||||
"DONE calling write_next_viewable, with new from_pos={}, \
|
||||
and remaining viewables len={}",
|
||||
next_from_pos.to_usize(),
|
||||
next_ordered_viewables.len()
|
||||
);
|
||||
assert!(
|
||||
from_pos != next_from_pos || ordered_viewables.len() != next_ordered_viewables.len(),
|
||||
"write_next_viewable_with_overlaps() must make a state change"
|
||||
);
|
||||
from_pos = next_from_pos;
|
||||
if next_ordered_viewables.len() != ordered_viewables.len() {
|
||||
ordered_viewables = next_ordered_viewables;
|
||||
if let Some(next_ordered_viewable) = ordered_viewables.first() {
|
||||
if &next_ordered_viewable.id != curr_id {
|
||||
alt = !alt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if from_pos < end_pos {
|
||||
write_coverage_gap(tcx, from_pos, end_pos, w)?;
|
||||
}
|
||||
writeln!(w, r#"</span></div>"#)?;
|
||||
writeln!(w, "{FOOTER}")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Format a string showing the start line and column, and end line and column within a file.
|
||||
pub fn source_range_no_file(tcx: TyCtxt<'_>, span: Span) -> String {
|
||||
let source_map = tcx.sess.source_map();
|
||||
let start = source_map.lookup_char_pos(span.lo());
|
||||
let end = source_map.lookup_char_pos(span.hi());
|
||||
format!("{}:{}-{}:{}", start.line, start.col.to_usize() + 1, end.line, end.col.to_usize() + 1)
|
||||
}
|
||||
|
||||
fn statement_span_viewable<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body_span: Span,
|
||||
bb: BasicBlock,
|
||||
i: usize,
|
||||
statement: &Statement<'tcx>,
|
||||
) -> Option<SpanViewable> {
|
||||
let span = statement.source_info.span;
|
||||
if !body_span.contains(span) {
|
||||
return None;
|
||||
}
|
||||
let id = format!("{}[{}]", bb.index(), i);
|
||||
let tooltip = tooltip(tcx, &id, span, vec![statement.clone()], &None);
|
||||
Some(SpanViewable { bb, span, id, tooltip })
|
||||
}
|
||||
|
||||
fn terminator_span_viewable<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body_span: Span,
|
||||
bb: BasicBlock,
|
||||
data: &BasicBlockData<'tcx>,
|
||||
) -> Option<SpanViewable> {
|
||||
let term = data.terminator();
|
||||
let span = term.source_info.span;
|
||||
if !body_span.contains(span) {
|
||||
return None;
|
||||
}
|
||||
let id = format!("{}:{}", bb.index(), term.kind.name());
|
||||
let tooltip = tooltip(tcx, &id, span, vec![], &data.terminator);
|
||||
Some(SpanViewable { bb, span, id, tooltip })
|
||||
}
|
||||
|
||||
fn block_span_viewable<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body_span: Span,
|
||||
bb: BasicBlock,
|
||||
data: &BasicBlockData<'tcx>,
|
||||
) -> Option<SpanViewable> {
|
||||
let span = compute_block_span(data, body_span);
|
||||
if !body_span.contains(span) {
|
||||
return None;
|
||||
}
|
||||
let id = format!("{}", bb.index());
|
||||
let tooltip = tooltip(tcx, &id, span, data.statements.clone(), &data.terminator);
|
||||
Some(SpanViewable { bb, span, id, tooltip })
|
||||
}
|
||||
|
||||
fn compute_block_span(data: &BasicBlockData<'_>, body_span: Span) -> Span {
|
||||
let mut span = data.terminator().source_info.span;
|
||||
for statement_span in data.statements.iter().map(|statement| statement.source_info.span) {
|
||||
// Only combine Spans from the root context, and within the function's body_span.
|
||||
if statement_span.ctxt().is_root() && body_span.contains(statement_span) {
|
||||
span = span.to(statement_span);
|
||||
}
|
||||
}
|
||||
span
|
||||
}
|
||||
|
||||
/// Recursively process each ordered span. Spans that overlap will have progressively varying
|
||||
/// styles, such as increased padding for each overlap. Non-overlapping adjacent spans will
|
||||
/// have alternating style choices, to help distinguish between them if, visually adjacent.
|
||||
/// The `layer` is incremented for each overlap, and the `alt` bool alternates between true
|
||||
/// and false, for each adjacent non-overlapping span. Source code between the spans (code
|
||||
/// that is not in any coverage region) has neutral styling.
|
||||
fn write_next_viewable_with_overlaps<'tcx, 'b, W>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
mut from_pos: BytePos,
|
||||
mut to_pos: BytePos,
|
||||
ordered_viewables: &'b [SpanViewable],
|
||||
alt: bool,
|
||||
layer: usize,
|
||||
w: &mut W,
|
||||
) -> io::Result<(BytePos, &'b [SpanViewable])>
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
let debug_indent = " ".repeat(layer);
|
||||
let (viewable, mut remaining_viewables) =
|
||||
ordered_viewables.split_first().expect("ordered_viewables should have some");
|
||||
|
||||
if from_pos < viewable.span.lo() {
|
||||
debug!(
|
||||
"{}advance from_pos to next SpanViewable (from from_pos={} to viewable.span.lo()={} \
|
||||
of {:?}), with to_pos={}",
|
||||
debug_indent,
|
||||
from_pos.to_usize(),
|
||||
viewable.span.lo().to_usize(),
|
||||
viewable.span,
|
||||
to_pos.to_usize()
|
||||
);
|
||||
let hi = cmp::min(viewable.span.lo(), to_pos);
|
||||
write_coverage_gap(tcx, from_pos, hi, w)?;
|
||||
from_pos = hi;
|
||||
if from_pos < viewable.span.lo() {
|
||||
debug!(
|
||||
"{}EARLY RETURN: stopped before getting to next SpanViewable, at {}",
|
||||
debug_indent,
|
||||
from_pos.to_usize()
|
||||
);
|
||||
return Ok((from_pos, ordered_viewables));
|
||||
}
|
||||
}
|
||||
|
||||
if from_pos < viewable.span.hi() {
|
||||
// Set to_pos to the end of this `viewable` to ensure the recursive calls stop writing
|
||||
// with room to print the tail.
|
||||
to_pos = cmp::min(viewable.span.hi(), to_pos);
|
||||
debug!(
|
||||
"{}update to_pos (if not closer) to viewable.span.hi()={}; to_pos is now {}",
|
||||
debug_indent,
|
||||
viewable.span.hi().to_usize(),
|
||||
to_pos.to_usize()
|
||||
);
|
||||
}
|
||||
|
||||
let mut subalt = false;
|
||||
while remaining_viewables.len() > 0 && remaining_viewables[0].span.overlaps(viewable.span) {
|
||||
let overlapping_viewable = &remaining_viewables[0];
|
||||
debug!("{}overlapping_viewable.span={:?}", debug_indent, overlapping_viewable.span);
|
||||
|
||||
let span =
|
||||
trim_span(viewable.span, from_pos, cmp::min(overlapping_viewable.span.lo(), to_pos));
|
||||
let mut some_html_snippet = if from_pos <= viewable.span.hi() || viewable.span.is_empty() {
|
||||
// `viewable` is not yet fully rendered, so start writing the span, up to either the
|
||||
// `to_pos` or the next `overlapping_viewable`, whichever comes first.
|
||||
debug!(
|
||||
"{}make html_snippet (may not write it if early exit) for partial span {:?} \
|
||||
of viewable.span {:?}",
|
||||
debug_indent, span, viewable.span
|
||||
);
|
||||
from_pos = span.hi();
|
||||
make_html_snippet(tcx, span, Some(viewable))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Defer writing the HTML snippet (until after early return checks) ONLY for empty spans.
|
||||
// An empty Span with Some(html_snippet) is probably a tail marker. If there is an early
|
||||
// exit, there should be another opportunity to write the tail marker.
|
||||
if !span.is_empty() {
|
||||
if let Some(ref html_snippet) = some_html_snippet {
|
||||
debug!(
|
||||
"{}write html_snippet for that partial span of viewable.span {:?}",
|
||||
debug_indent, viewable.span
|
||||
);
|
||||
write_span(html_snippet, &viewable.tooltip, alt, layer, w)?;
|
||||
}
|
||||
some_html_snippet = None;
|
||||
}
|
||||
|
||||
if from_pos < overlapping_viewable.span.lo() {
|
||||
debug!(
|
||||
"{}EARLY RETURN: from_pos={} has not yet reached the \
|
||||
overlapping_viewable.span {:?}",
|
||||
debug_indent,
|
||||
from_pos.to_usize(),
|
||||
overlapping_viewable.span
|
||||
);
|
||||
// must have reached `to_pos` before reaching the start of the
|
||||
// `overlapping_viewable.span`
|
||||
return Ok((from_pos, ordered_viewables));
|
||||
}
|
||||
|
||||
if from_pos == to_pos
|
||||
&& !(from_pos == overlapping_viewable.span.lo() && overlapping_viewable.span.is_empty())
|
||||
{
|
||||
debug!(
|
||||
"{}EARLY RETURN: from_pos=to_pos={} and overlapping_viewable.span {:?} is not \
|
||||
empty, or not from_pos",
|
||||
debug_indent,
|
||||
to_pos.to_usize(),
|
||||
overlapping_viewable.span
|
||||
);
|
||||
// `to_pos` must have occurred before the overlapping viewable. Return
|
||||
// `ordered_viewables` so we can continue rendering the `viewable`, from after the
|
||||
// `to_pos`.
|
||||
return Ok((from_pos, ordered_viewables));
|
||||
}
|
||||
|
||||
if let Some(ref html_snippet) = some_html_snippet {
|
||||
debug!(
|
||||
"{}write html_snippet for that partial span of viewable.span {:?}",
|
||||
debug_indent, viewable.span
|
||||
);
|
||||
write_span(html_snippet, &viewable.tooltip, alt, layer, w)?;
|
||||
}
|
||||
|
||||
debug!(
|
||||
"{}recursively calling write_next_viewable with from_pos={}, to_pos={}, \
|
||||
and viewables len={}",
|
||||
debug_indent,
|
||||
from_pos.to_usize(),
|
||||
to_pos.to_usize(),
|
||||
remaining_viewables.len()
|
||||
);
|
||||
// Write the overlaps (and the overlaps' overlaps, if any) up to `to_pos`.
|
||||
let curr_id = &remaining_viewables[0].id;
|
||||
let (next_from_pos, next_remaining_viewables) = write_next_viewable_with_overlaps(
|
||||
tcx,
|
||||
from_pos,
|
||||
to_pos,
|
||||
remaining_viewables,
|
||||
subalt,
|
||||
layer + 1,
|
||||
w,
|
||||
)?;
|
||||
debug!(
|
||||
"{}DONE recursively calling write_next_viewable, with new from_pos={}, and remaining \
|
||||
viewables len={}",
|
||||
debug_indent,
|
||||
next_from_pos.to_usize(),
|
||||
next_remaining_viewables.len()
|
||||
);
|
||||
assert!(
|
||||
from_pos != next_from_pos
|
||||
|| remaining_viewables.len() != next_remaining_viewables.len(),
|
||||
"write_next_viewable_with_overlaps() must make a state change"
|
||||
);
|
||||
from_pos = next_from_pos;
|
||||
if next_remaining_viewables.len() != remaining_viewables.len() {
|
||||
remaining_viewables = next_remaining_viewables;
|
||||
if let Some(next_ordered_viewable) = remaining_viewables.first() {
|
||||
if &next_ordered_viewable.id != curr_id {
|
||||
subalt = !subalt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if from_pos <= viewable.span.hi() {
|
||||
let span = trim_span(viewable.span, from_pos, to_pos);
|
||||
debug!(
|
||||
"{}After overlaps, writing (end span?) {:?} of viewable.span {:?}",
|
||||
debug_indent, span, viewable.span
|
||||
);
|
||||
if let Some(ref html_snippet) = make_html_snippet(tcx, span, Some(viewable)) {
|
||||
from_pos = span.hi();
|
||||
write_span(html_snippet, &viewable.tooltip, alt, layer, w)?;
|
||||
}
|
||||
}
|
||||
debug!("{}RETURN: No more overlap", debug_indent);
|
||||
Ok((
|
||||
from_pos,
|
||||
if from_pos < viewable.span.hi() { ordered_viewables } else { remaining_viewables },
|
||||
))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_coverage_gap<W>(tcx: TyCtxt<'_>, lo: BytePos, hi: BytePos, w: &mut W) -> io::Result<()>
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
let span = Span::with_root_ctxt(lo, hi);
|
||||
if let Some(ref html_snippet) = make_html_snippet(tcx, span, None) {
|
||||
write_span(html_snippet, "", false, 0, w)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn write_span<W>(
|
||||
html_snippet: &str,
|
||||
tooltip: &str,
|
||||
alt: bool,
|
||||
layer: usize,
|
||||
w: &mut W,
|
||||
) -> io::Result<()>
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
let maybe_alt_class = if layer > 0 { if alt { " odd" } else { " even" } } else { "" };
|
||||
let maybe_title_attr = if !tooltip.is_empty() {
|
||||
format!(" title=\"{}\"", escape_attr(tooltip))
|
||||
} else {
|
||||
"".to_owned()
|
||||
};
|
||||
if layer == 1 {
|
||||
write!(w, "<span>")?;
|
||||
}
|
||||
for (i, line) in html_snippet.lines().enumerate() {
|
||||
if i > 0 {
|
||||
write!(w, "{NEW_LINE_SPAN}")?;
|
||||
}
|
||||
write!(
|
||||
w,
|
||||
r#"<span class="code{maybe_alt_class}" style="--layer: {layer}"{maybe_title_attr}>{line}</span>"#
|
||||
)?;
|
||||
}
|
||||
// Check for and translate trailing newlines, because `str::lines()` ignores them
|
||||
if html_snippet.ends_with('\n') {
|
||||
write!(w, "{NEW_LINE_SPAN}")?;
|
||||
}
|
||||
if layer == 1 {
|
||||
write!(w, "</span>")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn make_html_snippet(
|
||||
tcx: TyCtxt<'_>,
|
||||
span: Span,
|
||||
some_viewable: Option<&SpanViewable>,
|
||||
) -> Option<String> {
|
||||
let source_map = tcx.sess.source_map();
|
||||
let snippet = source_map
|
||||
.span_to_snippet(span)
|
||||
.unwrap_or_else(|err| bug!("span_to_snippet error for span {:?}: {:?}", span, err));
|
||||
let html_snippet = if let Some(viewable) = some_viewable {
|
||||
let is_head = span.lo() == viewable.span.lo();
|
||||
let is_tail = span.hi() == viewable.span.hi();
|
||||
let mut labeled_snippet = if is_head {
|
||||
format!(r#"<span class="annotation">{}{}</span>"#, viewable.id, ANNOTATION_LEFT_BRACKET)
|
||||
} else {
|
||||
"".to_owned()
|
||||
};
|
||||
if span.is_empty() {
|
||||
if is_head && is_tail {
|
||||
labeled_snippet.push(CARET);
|
||||
}
|
||||
} else {
|
||||
labeled_snippet.push_str(&escape_html(&snippet));
|
||||
};
|
||||
if is_tail {
|
||||
labeled_snippet.push_str(&format!(
|
||||
r#"<span class="annotation">{}{}</span>"#,
|
||||
ANNOTATION_RIGHT_BRACKET, viewable.id
|
||||
));
|
||||
}
|
||||
labeled_snippet
|
||||
} else {
|
||||
escape_html(&snippet)
|
||||
};
|
||||
if html_snippet.is_empty() { None } else { Some(html_snippet) }
|
||||
}
|
||||
|
||||
fn tooltip<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
spanview_id: &str,
|
||||
span: Span,
|
||||
statements: Vec<Statement<'tcx>>,
|
||||
terminator: &Option<Terminator<'tcx>>,
|
||||
) -> String {
|
||||
let source_map = tcx.sess.source_map();
|
||||
let mut text = Vec::new();
|
||||
text.push(format!("{}: {}:", spanview_id, &source_map.span_to_embeddable_string(span)));
|
||||
for statement in statements {
|
||||
let source_range = source_range_no_file(tcx, statement.source_info.span);
|
||||
text.push(format!(
|
||||
"\n{}{}: {}: {:?}",
|
||||
TOOLTIP_INDENT,
|
||||
source_range,
|
||||
statement.kind.name(),
|
||||
statement
|
||||
));
|
||||
}
|
||||
if let Some(term) = terminator {
|
||||
let source_range = source_range_no_file(tcx, term.source_info.span);
|
||||
text.push(format!(
|
||||
"\n{}{}: {}: {:?}",
|
||||
TOOLTIP_INDENT,
|
||||
source_range,
|
||||
term.kind.name(),
|
||||
term.kind
|
||||
));
|
||||
}
|
||||
text.join("")
|
||||
}
|
||||
|
||||
fn trim_span(span: Span, from_pos: BytePos, to_pos: BytePos) -> Span {
|
||||
trim_span_hi(trim_span_lo(span, from_pos), to_pos)
|
||||
}
|
||||
|
||||
fn trim_span_lo(span: Span, from_pos: BytePos) -> Span {
|
||||
if from_pos <= span.lo() { span } else { span.with_lo(cmp::min(span.hi(), from_pos)) }
|
||||
}
|
||||
|
||||
fn trim_span_hi(span: Span, to_pos: BytePos) -> Span {
|
||||
if to_pos >= span.hi() { span } else { span.with_hi(cmp::max(span.lo(), to_pos)) }
|
||||
}
|
||||
|
||||
fn fn_span(tcx: TyCtxt<'_>, def_id: DefId) -> Span {
|
||||
let fn_decl_span = tcx.def_span(def_id);
|
||||
if let Some(body_span) = hir_body(tcx, def_id).map(|hir_body| hir_body.value.span) {
|
||||
if fn_decl_span.eq_ctxt(body_span) { fn_decl_span.to(body_span) } else { body_span }
|
||||
} else {
|
||||
fn_decl_span
|
||||
}
|
||||
}
|
||||
|
||||
fn hir_body(tcx: TyCtxt<'_>, def_id: DefId) -> Option<&rustc_hir::Body<'_>> {
|
||||
let hir_node = tcx.hir().get_if_local(def_id).expect("expected DefId is local");
|
||||
hir::map::associated_body(hir_node).map(|(_, fn_body_id)| tcx.hir().body(fn_body_id))
|
||||
}
|
||||
|
||||
fn escape_html(s: &str) -> String {
|
||||
s.replace('&', "&").replace('<', "<").replace('>', ">")
|
||||
}
|
||||
|
||||
fn escape_attr(s: &str) -> String {
|
||||
s.replace('&', "&")
|
||||
.replace('\"', """)
|
||||
.replace('\'', "'")
|
||||
.replace('<', "<")
|
||||
.replace('>', ">")
|
||||
}
|
@ -125,21 +125,6 @@ pub enum LtoCli {
|
||||
Unspecified,
|
||||
}
|
||||
|
||||
/// The different settings that the `-Z dump_mir_spanview` flag can have. `Statement` generates a
|
||||
/// document highlighting each span of every statement (including terminators). `Terminator` and
|
||||
/// `Block` highlight a single span per `BasicBlock`: the span of the block's `Terminator`, or a
|
||||
/// computed span for the block, representing the entire range, covering the block's terminator and
|
||||
/// all of its statements.
|
||||
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
|
||||
pub enum MirSpanview {
|
||||
/// Default `-Z dump_mir_spanview` or `-Z dump_mir_spanview=statement`
|
||||
Statement,
|
||||
/// `-Z dump_mir_spanview=terminator`
|
||||
Terminator,
|
||||
/// `-Z dump_mir_spanview=block`
|
||||
Block,
|
||||
}
|
||||
|
||||
/// The different settings that the `-C instrument-coverage` flag can have.
|
||||
///
|
||||
/// Coverage instrumentation now supports combining `-C instrument-coverage`
|
||||
|
@ -391,7 +391,6 @@ mod desc {
|
||||
pub const parse_strip: &str = "either `none`, `debuginfo`, or `symbols`";
|
||||
pub const parse_linker_flavor: &str = ::rustc_target::spec::LinkerFlavorCli::one_of();
|
||||
pub const parse_optimization_fuel: &str = "crate=integer";
|
||||
pub const parse_mir_spanview: &str = "`statement` (default), `terminator`, or `block`";
|
||||
pub const parse_dump_mono_stats: &str = "`markdown` (default) or `json`";
|
||||
pub const parse_instrument_coverage: &str =
|
||||
"`all` (default), `branch`, `except-unused-generics`, `except-unused-functions`, or `off`";
|
||||
@ -866,29 +865,6 @@ pub(crate) fn parse_unpretty(slot: &mut Option<String>, v: Option<&str>) -> bool
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn parse_mir_spanview(slot: &mut Option<MirSpanview>, v: Option<&str>) -> bool {
|
||||
if v.is_some() {
|
||||
let mut bool_arg = None;
|
||||
if parse_opt_bool(&mut bool_arg, v) {
|
||||
*slot = bool_arg.unwrap().then_some(MirSpanview::Statement);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
let Some(v) = v else {
|
||||
*slot = Some(MirSpanview::Statement);
|
||||
return true;
|
||||
};
|
||||
|
||||
*slot = Some(match v.trim_end_matches('s') {
|
||||
"statement" | "stmt" => MirSpanview::Statement,
|
||||
"terminator" | "term" => MirSpanview::Terminator,
|
||||
"block" | "basicblock" => MirSpanview::Block,
|
||||
_ => return false,
|
||||
});
|
||||
true
|
||||
}
|
||||
|
||||
pub(crate) fn parse_time_passes_format(slot: &mut TimePassesFormat, v: Option<&str>) -> bool {
|
||||
match v {
|
||||
None => true,
|
||||
@ -1601,11 +1577,6 @@ pub(crate) fn parse_function_return(slot: &mut FunctionReturn, v: Option<&str>)
|
||||
"exclude the pass number when dumping MIR (used in tests) (default: no)"),
|
||||
dump_mir_graphviz: bool = (false, parse_bool, [UNTRACKED],
|
||||
"in addition to `.mir` files, create graphviz `.dot` files (default: no)"),
|
||||
dump_mir_spanview: Option<MirSpanview> = (None, parse_mir_spanview, [UNTRACKED],
|
||||
"in addition to `.mir` files, create `.html` files to view spans for \
|
||||
all `statement`s (including terminators), only `terminator` spans, or \
|
||||
computed `block` spans (one span encompassing a block's terminator and \
|
||||
all statements)."),
|
||||
dump_mono_stats: SwitchWithOptPath = (SwitchWithOptPath::Disabled,
|
||||
parse_switch_with_opt_path, [UNTRACKED],
|
||||
"output statistics about monomorphization collection"),
|
||||
|
@ -1,67 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>spanview_block.main.built.after</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 5"><span class="line"><span class="code" style="--layer: 0">fn main() </span><span><span class="code even" style="--layer: 1" title="0: $DIR/spanview_block.rs:6:11: 6:13:
|
||||
6:11-6:13: Assign: _0 = const ()
|
||||
6:13-6:13: Return: return"><span class="annotation">0⦊</span>{}<span class="annotation">⦉0</span></span></span></span></div>
|
||||
</body>
|
||||
</html>
|
@ -1,6 +0,0 @@
|
||||
// skip-filecheck
|
||||
// Test spanview block output
|
||||
// compile-flags: -Z dump-mir-spanview=block
|
||||
|
||||
// EMIT_MIR spanview_block.main.built.after.html
|
||||
fn main() {}
|
@ -1,67 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>spanview_statement.main.built.after</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 5"><span class="line"><span class="code" style="--layer: 0">fn main() </span><span><span class="code even" style="--layer: 1" title="0[0]: $DIR/spanview_statement.rs:6:11: 6:13:
|
||||
6:11-6:13: Assign: _0 = const ()"><span class="annotation">0[0]⦊</span>{}<span class="annotation">⦉0[0]</span></span></span><span><span class="code odd" style="--layer: 1" title="0:Return: $DIR/spanview_statement.rs:6:13: 6:13:
|
||||
6:13-6:13: Return: return"><span class="annotation">0:Return⦊</span>‸<span class="annotation">⦉0:Return</span></span></span></span></div>
|
||||
</body>
|
||||
</html>
|
@ -1,6 +0,0 @@
|
||||
// skip-filecheck
|
||||
// Test spanview output (the default value for `-Z dump-mir-spanview` is "statement")
|
||||
// compile-flags: -Z dump-mir-spanview
|
||||
|
||||
// EMIT_MIR spanview_statement.main.built.after.html
|
||||
fn main() {}
|
@ -1,66 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>spanview_terminator.main.built.after</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 5"><span class="line"><span class="code" style="--layer: 0">fn main() {}</span><span><span class="code even" style="--layer: 1" title="0:Return: $DIR/spanview_terminator.rs:6:13: 6:13:
|
||||
6:13-6:13: Return: return"><span class="annotation">0:Return⦊</span>‸<span class="annotation">⦉0:Return</span></span></span></span></div>
|
||||
</body>
|
||||
</html>
|
@ -1,6 +0,0 @@
|
||||
// skip-filecheck
|
||||
// Test spanview terminator output
|
||||
// compile-flags: -Z dump-mir-spanview=terminator
|
||||
|
||||
// EMIT_MIR spanview_terminator.main.built.after.html
|
||||
fn main() {}
|
Loading…
Reference in New Issue
Block a user