1 use rustc_hir::def_id::DefId;
2 use rustc_middle::hir;
3 use rustc_middle::mir::*;
4 use rustc_middle::ty::TyCtxt;
5 use rustc_session::config::MirSpanview;
6 use rustc_span::{BytePos, Pos, Span};
7
8 use std::cmp;
9 use std::io::{self, Write};
10
11 pub const TOOLTIP_INDENT: &str = " ";
12
13 const CARET: char = '\u{2038}'; // Unicode `CARET`
14 const ANNOTATION_LEFT_BRACKET: char = '\u{298a}'; // Unicode `Z NOTATION RIGHT BINDING BRACKET`
15 const ANNOTATION_RIGHT_BRACKET: char = '\u{2989}'; // Unicode `Z NOTATION LEFT BINDING BRACKET`
16 const NEW_LINE_SPAN: &str = "</span>\n<span class=\"line\">";
17 const HEADER: &str = r#"<!DOCTYPE html>
18 <html lang="en">
19 <head>
20 <meta charset="utf-8">"#;
21 const START_BODY: &str = r#"</head>
22 <body>"#;
23 const FOOTER: &str = r#"</body>
24 </html>"#;
25
26 const STYLE_SECTION: &str = r#"<style>
27 .line {
28 counter-increment: line;
29 }
30 .line:before {
31 content: counter(line) ": ";
32 font-family: Menlo, Monaco, monospace;
33 font-style: italic;
34 width: 3.8em;
35 display: inline-block;
36 text-align: right;
37 filter: opacity(50%);
38 -webkit-user-select: none;
39 }
40 .code {
41 color: #dddddd;
42 background-color: #222222;
43 font-family: Menlo, Monaco, monospace;
44 line-height: 1.4em;
45 border-bottom: 2px solid #222222;
46 white-space: pre;
47 display: inline-block;
48 }
49 .odd {
50 background-color: #55bbff;
51 color: #223311;
52 }
53 .even {
54 background-color: #ee7756;
55 color: #551133;
56 }
57 .code {
58 --index: calc(var(--layer) - 1);
59 padding-top: calc(var(--index) * 0.15em);
60 filter:
61 hue-rotate(calc(var(--index) * 25deg))
62 saturate(calc(100% - (var(--index) * 2%)))
63 brightness(calc(100% - (var(--index) * 1.5%)));
64 }
65 .annotation {
66 color: #4444ff;
67 font-family: monospace;
68 font-style: italic;
69 display: none;
70 -webkit-user-select: none;
71 }
72 body:active .annotation {
73 /* requires holding mouse down anywhere on the page */
74 display: inline-block;
75 }
76 span:hover .annotation {
77 /* requires hover over a span ONLY on its first line */
78 display: inline-block;
79 }
80 </style>"#;
81
82 /// Metadata to highlight the span of a MIR BasicBlock, Statement, or Terminator.
83 #[derive(Clone, Debug)]
84 pub struct SpanViewable {
85 pub bb: BasicBlock,
86 pub span: Span,
87 pub id: String,
88 pub tooltip: String,
89 }
90
91 /// Write a spanview HTML+CSS file to analyze MIR element spans.
write_mir_fn_spanview<'tcx, W>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, spanview: MirSpanview, title: &str, w: &mut W, ) -> io::Result<()> where W: Write,92 pub fn write_mir_fn_spanview<'tcx, W>(
93 tcx: TyCtxt<'tcx>,
94 body: &Body<'tcx>,
95 spanview: MirSpanview,
96 title: &str,
97 w: &mut W,
98 ) -> io::Result<()>
99 where
100 W: Write,
101 {
102 let def_id = body.source.def_id();
103 let hir_body = hir_body(tcx, def_id);
104 if hir_body.is_none() {
105 return Ok(());
106 }
107 let body_span = hir_body.unwrap().value.span;
108 let mut span_viewables = Vec::new();
109 for (bb, data) in body.basic_blocks.iter_enumerated() {
110 match spanview {
111 MirSpanview::Statement => {
112 for (i, statement) in data.statements.iter().enumerate() {
113 if let Some(span_viewable) =
114 statement_span_viewable(tcx, body_span, bb, i, statement)
115 {
116 span_viewables.push(span_viewable);
117 }
118 }
119 if let Some(span_viewable) = terminator_span_viewable(tcx, body_span, bb, data) {
120 span_viewables.push(span_viewable);
121 }
122 }
123 MirSpanview::Terminator => {
124 if let Some(span_viewable) = terminator_span_viewable(tcx, body_span, bb, data) {
125 span_viewables.push(span_viewable);
126 }
127 }
128 MirSpanview::Block => {
129 if let Some(span_viewable) = block_span_viewable(tcx, body_span, bb, data) {
130 span_viewables.push(span_viewable);
131 }
132 }
133 }
134 }
135 write_document(tcx, fn_span(tcx, def_id), span_viewables, title, w)?;
136 Ok(())
137 }
138
139 /// Generate a spanview HTML+CSS document for the given local function `def_id`, and a pre-generated
140 /// list `SpanViewable`s.
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,141 pub fn write_document<'tcx, W>(
142 tcx: TyCtxt<'tcx>,
143 spanview_span: Span,
144 mut span_viewables: Vec<SpanViewable>,
145 title: &str,
146 w: &mut W,
147 ) -> io::Result<()>
148 where
149 W: Write,
150 {
151 let mut from_pos = spanview_span.lo();
152 let end_pos = spanview_span.hi();
153 let source_map = tcx.sess.source_map();
154 let start = source_map.lookup_char_pos(from_pos);
155 let indent_to_initial_start_col = " ".repeat(start.col.to_usize());
156 debug!(
157 "spanview_span={:?}; source is:\n{}{}",
158 spanview_span,
159 indent_to_initial_start_col,
160 source_map.span_to_snippet(spanview_span).expect("function should have printable source")
161 );
162 writeln!(w, "{}", HEADER)?;
163 writeln!(w, "<title>{}</title>", title)?;
164 writeln!(w, "{}", STYLE_SECTION)?;
165 writeln!(w, "{}", START_BODY)?;
166 write!(
167 w,
168 r#"<div class="code" style="counter-reset: line {}"><span class="line">{}"#,
169 start.line - 1,
170 indent_to_initial_start_col,
171 )?;
172 span_viewables.sort_unstable_by(|a, b| {
173 let a = a.span;
174 let b = b.span;
175 if a.lo() == b.lo() {
176 // Sort hi() in reverse order so shorter spans are attempted after longer spans.
177 // This should give shorter spans a higher "layer", so they are not covered by
178 // the longer spans.
179 b.hi().partial_cmp(&a.hi())
180 } else {
181 a.lo().partial_cmp(&b.lo())
182 }
183 .unwrap()
184 });
185 let mut ordered_viewables = &span_viewables[..];
186 const LOWEST_VIEWABLE_LAYER: usize = 1;
187 let mut alt = false;
188 while ordered_viewables.len() > 0 {
189 debug!(
190 "calling write_next_viewable with from_pos={}, end_pos={}, and viewables len={}",
191 from_pos.to_usize(),
192 end_pos.to_usize(),
193 ordered_viewables.len()
194 );
195 let curr_id = &ordered_viewables[0].id;
196 let (next_from_pos, next_ordered_viewables) = write_next_viewable_with_overlaps(
197 tcx,
198 from_pos,
199 end_pos,
200 ordered_viewables,
201 alt,
202 LOWEST_VIEWABLE_LAYER,
203 w,
204 )?;
205 debug!(
206 "DONE calling write_next_viewable, with new from_pos={}, \
207 and remaining viewables len={}",
208 next_from_pos.to_usize(),
209 next_ordered_viewables.len()
210 );
211 assert!(
212 from_pos != next_from_pos || ordered_viewables.len() != next_ordered_viewables.len(),
213 "write_next_viewable_with_overlaps() must make a state change"
214 );
215 from_pos = next_from_pos;
216 if next_ordered_viewables.len() != ordered_viewables.len() {
217 ordered_viewables = next_ordered_viewables;
218 if let Some(next_ordered_viewable) = ordered_viewables.first() {
219 if &next_ordered_viewable.id != curr_id {
220 alt = !alt;
221 }
222 }
223 }
224 }
225 if from_pos < end_pos {
226 write_coverage_gap(tcx, from_pos, end_pos, w)?;
227 }
228 writeln!(w, r#"</span></div>"#)?;
229 writeln!(w, "{}", FOOTER)?;
230 Ok(())
231 }
232
233 /// Format a string showing the start line and column, and end line and column within a file.
source_range_no_file(tcx: TyCtxt<'_>, span: Span) -> String234 pub fn source_range_no_file(tcx: TyCtxt<'_>, span: Span) -> String {
235 let source_map = tcx.sess.source_map();
236 let start = source_map.lookup_char_pos(span.lo());
237 let end = source_map.lookup_char_pos(span.hi());
238 format!("{}:{}-{}:{}", start.line, start.col.to_usize() + 1, end.line, end.col.to_usize() + 1)
239 }
240
statement_kind_name(statement: &Statement<'_>) -> &'static str241 pub fn statement_kind_name(statement: &Statement<'_>) -> &'static str {
242 use StatementKind::*;
243 match statement.kind {
244 Assign(..) => "Assign",
245 FakeRead(..) => "FakeRead",
246 SetDiscriminant { .. } => "SetDiscriminant",
247 Deinit(..) => "Deinit",
248 StorageLive(..) => "StorageLive",
249 StorageDead(..) => "StorageDead",
250 Retag(..) => "Retag",
251 PlaceMention(..) => "PlaceMention",
252 AscribeUserType(..) => "AscribeUserType",
253 Coverage(..) => "Coverage",
254 Intrinsic(..) => "Intrinsic",
255 ConstEvalCounter => "ConstEvalCounter",
256 Nop => "Nop",
257 }
258 }
259
terminator_kind_name(term: &Terminator<'_>) -> &'static str260 pub fn terminator_kind_name(term: &Terminator<'_>) -> &'static str {
261 use TerminatorKind::*;
262 match term.kind {
263 Goto { .. } => "Goto",
264 SwitchInt { .. } => "SwitchInt",
265 Resume => "Resume",
266 Terminate => "Terminate",
267 Return => "Return",
268 Unreachable => "Unreachable",
269 Drop { .. } => "Drop",
270 Call { .. } => "Call",
271 Assert { .. } => "Assert",
272 Yield { .. } => "Yield",
273 GeneratorDrop => "GeneratorDrop",
274 FalseEdge { .. } => "FalseEdge",
275 FalseUnwind { .. } => "FalseUnwind",
276 InlineAsm { .. } => "InlineAsm",
277 }
278 }
279
statement_span_viewable<'tcx>( tcx: TyCtxt<'tcx>, body_span: Span, bb: BasicBlock, i: usize, statement: &Statement<'tcx>, ) -> Option<SpanViewable>280 fn statement_span_viewable<'tcx>(
281 tcx: TyCtxt<'tcx>,
282 body_span: Span,
283 bb: BasicBlock,
284 i: usize,
285 statement: &Statement<'tcx>,
286 ) -> Option<SpanViewable> {
287 let span = statement.source_info.span;
288 if !body_span.contains(span) {
289 return None;
290 }
291 let id = format!("{}[{}]", bb.index(), i);
292 let tooltip = tooltip(tcx, &id, span, vec![statement.clone()], &None);
293 Some(SpanViewable { bb, span, id, tooltip })
294 }
295
terminator_span_viewable<'tcx>( tcx: TyCtxt<'tcx>, body_span: Span, bb: BasicBlock, data: &BasicBlockData<'tcx>, ) -> Option<SpanViewable>296 fn terminator_span_viewable<'tcx>(
297 tcx: TyCtxt<'tcx>,
298 body_span: Span,
299 bb: BasicBlock,
300 data: &BasicBlockData<'tcx>,
301 ) -> Option<SpanViewable> {
302 let term = data.terminator();
303 let span = term.source_info.span;
304 if !body_span.contains(span) {
305 return None;
306 }
307 let id = format!("{}:{}", bb.index(), terminator_kind_name(term));
308 let tooltip = tooltip(tcx, &id, span, vec![], &data.terminator);
309 Some(SpanViewable { bb, span, id, tooltip })
310 }
311
block_span_viewable<'tcx>( tcx: TyCtxt<'tcx>, body_span: Span, bb: BasicBlock, data: &BasicBlockData<'tcx>, ) -> Option<SpanViewable>312 fn block_span_viewable<'tcx>(
313 tcx: TyCtxt<'tcx>,
314 body_span: Span,
315 bb: BasicBlock,
316 data: &BasicBlockData<'tcx>,
317 ) -> Option<SpanViewable> {
318 let span = compute_block_span(data, body_span);
319 if !body_span.contains(span) {
320 return None;
321 }
322 let id = format!("{}", bb.index());
323 let tooltip = tooltip(tcx, &id, span, data.statements.clone(), &data.terminator);
324 Some(SpanViewable { bb, span, id, tooltip })
325 }
326
compute_block_span(data: &BasicBlockData<'_>, body_span: Span) -> Span327 fn compute_block_span(data: &BasicBlockData<'_>, body_span: Span) -> Span {
328 let mut span = data.terminator().source_info.span;
329 for statement_span in data.statements.iter().map(|statement| statement.source_info.span) {
330 // Only combine Spans from the root context, and within the function's body_span.
331 if statement_span.ctxt().is_root() && body_span.contains(statement_span) {
332 span = span.to(statement_span);
333 }
334 }
335 span
336 }
337
338 /// Recursively process each ordered span. Spans that overlap will have progressively varying
339 /// styles, such as increased padding for each overlap. Non-overlapping adjacent spans will
340 /// have alternating style choices, to help distinguish between them if, visually adjacent.
341 /// The `layer` is incremented for each overlap, and the `alt` bool alternates between true
342 /// and false, for each adjacent non-overlapping span. Source code between the spans (code
343 /// that is not in any coverage region) has neutral styling.
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,344 fn write_next_viewable_with_overlaps<'tcx, 'b, W>(
345 tcx: TyCtxt<'tcx>,
346 mut from_pos: BytePos,
347 mut to_pos: BytePos,
348 ordered_viewables: &'b [SpanViewable],
349 alt: bool,
350 layer: usize,
351 w: &mut W,
352 ) -> io::Result<(BytePos, &'b [SpanViewable])>
353 where
354 W: Write,
355 {
356 let debug_indent = " ".repeat(layer);
357 let (viewable, mut remaining_viewables) =
358 ordered_viewables.split_first().expect("ordered_viewables should have some");
359
360 if from_pos < viewable.span.lo() {
361 debug!(
362 "{}advance from_pos to next SpanViewable (from from_pos={} to viewable.span.lo()={} \
363 of {:?}), with to_pos={}",
364 debug_indent,
365 from_pos.to_usize(),
366 viewable.span.lo().to_usize(),
367 viewable.span,
368 to_pos.to_usize()
369 );
370 let hi = cmp::min(viewable.span.lo(), to_pos);
371 write_coverage_gap(tcx, from_pos, hi, w)?;
372 from_pos = hi;
373 if from_pos < viewable.span.lo() {
374 debug!(
375 "{}EARLY RETURN: stopped before getting to next SpanViewable, at {}",
376 debug_indent,
377 from_pos.to_usize()
378 );
379 return Ok((from_pos, ordered_viewables));
380 }
381 }
382
383 if from_pos < viewable.span.hi() {
384 // Set to_pos to the end of this `viewable` to ensure the recursive calls stop writing
385 // with room to print the tail.
386 to_pos = cmp::min(viewable.span.hi(), to_pos);
387 debug!(
388 "{}update to_pos (if not closer) to viewable.span.hi()={}; to_pos is now {}",
389 debug_indent,
390 viewable.span.hi().to_usize(),
391 to_pos.to_usize()
392 );
393 }
394
395 let mut subalt = false;
396 while remaining_viewables.len() > 0 && remaining_viewables[0].span.overlaps(viewable.span) {
397 let overlapping_viewable = &remaining_viewables[0];
398 debug!("{}overlapping_viewable.span={:?}", debug_indent, overlapping_viewable.span);
399
400 let span =
401 trim_span(viewable.span, from_pos, cmp::min(overlapping_viewable.span.lo(), to_pos));
402 let mut some_html_snippet = if from_pos <= viewable.span.hi() || viewable.span.is_empty() {
403 // `viewable` is not yet fully rendered, so start writing the span, up to either the
404 // `to_pos` or the next `overlapping_viewable`, whichever comes first.
405 debug!(
406 "{}make html_snippet (may not write it if early exit) for partial span {:?} \
407 of viewable.span {:?}",
408 debug_indent, span, viewable.span
409 );
410 from_pos = span.hi();
411 make_html_snippet(tcx, span, Some(&viewable))
412 } else {
413 None
414 };
415
416 // Defer writing the HTML snippet (until after early return checks) ONLY for empty spans.
417 // An empty Span with Some(html_snippet) is probably a tail marker. If there is an early
418 // exit, there should be another opportunity to write the tail marker.
419 if !span.is_empty() {
420 if let Some(ref html_snippet) = some_html_snippet {
421 debug!(
422 "{}write html_snippet for that partial span of viewable.span {:?}",
423 debug_indent, viewable.span
424 );
425 write_span(html_snippet, &viewable.tooltip, alt, layer, w)?;
426 }
427 some_html_snippet = None;
428 }
429
430 if from_pos < overlapping_viewable.span.lo() {
431 debug!(
432 "{}EARLY RETURN: from_pos={} has not yet reached the \
433 overlapping_viewable.span {:?}",
434 debug_indent,
435 from_pos.to_usize(),
436 overlapping_viewable.span
437 );
438 // must have reached `to_pos` before reaching the start of the
439 // `overlapping_viewable.span`
440 return Ok((from_pos, ordered_viewables));
441 }
442
443 if from_pos == to_pos
444 && !(from_pos == overlapping_viewable.span.lo() && overlapping_viewable.span.is_empty())
445 {
446 debug!(
447 "{}EARLY RETURN: from_pos=to_pos={} and overlapping_viewable.span {:?} is not \
448 empty, or not from_pos",
449 debug_indent,
450 to_pos.to_usize(),
451 overlapping_viewable.span
452 );
453 // `to_pos` must have occurred before the overlapping viewable. Return
454 // `ordered_viewables` so we can continue rendering the `viewable`, from after the
455 // `to_pos`.
456 return Ok((from_pos, ordered_viewables));
457 }
458
459 if let Some(ref html_snippet) = some_html_snippet {
460 debug!(
461 "{}write html_snippet for that partial span of viewable.span {:?}",
462 debug_indent, viewable.span
463 );
464 write_span(html_snippet, &viewable.tooltip, alt, layer, w)?;
465 }
466
467 debug!(
468 "{}recursively calling write_next_viewable with from_pos={}, to_pos={}, \
469 and viewables len={}",
470 debug_indent,
471 from_pos.to_usize(),
472 to_pos.to_usize(),
473 remaining_viewables.len()
474 );
475 // Write the overlaps (and the overlaps' overlaps, if any) up to `to_pos`.
476 let curr_id = &remaining_viewables[0].id;
477 let (next_from_pos, next_remaining_viewables) = write_next_viewable_with_overlaps(
478 tcx,
479 from_pos,
480 to_pos,
481 &remaining_viewables,
482 subalt,
483 layer + 1,
484 w,
485 )?;
486 debug!(
487 "{}DONE recursively calling write_next_viewable, with new from_pos={}, and remaining \
488 viewables len={}",
489 debug_indent,
490 next_from_pos.to_usize(),
491 next_remaining_viewables.len()
492 );
493 assert!(
494 from_pos != next_from_pos
495 || remaining_viewables.len() != next_remaining_viewables.len(),
496 "write_next_viewable_with_overlaps() must make a state change"
497 );
498 from_pos = next_from_pos;
499 if next_remaining_viewables.len() != remaining_viewables.len() {
500 remaining_viewables = next_remaining_viewables;
501 if let Some(next_ordered_viewable) = remaining_viewables.first() {
502 if &next_ordered_viewable.id != curr_id {
503 subalt = !subalt;
504 }
505 }
506 }
507 }
508 if from_pos <= viewable.span.hi() {
509 let span = trim_span(viewable.span, from_pos, to_pos);
510 debug!(
511 "{}After overlaps, writing (end span?) {:?} of viewable.span {:?}",
512 debug_indent, span, viewable.span
513 );
514 if let Some(ref html_snippet) = make_html_snippet(tcx, span, Some(&viewable)) {
515 from_pos = span.hi();
516 write_span(html_snippet, &viewable.tooltip, alt, layer, w)?;
517 }
518 }
519 debug!("{}RETURN: No more overlap", debug_indent);
520 Ok((
521 from_pos,
522 if from_pos < viewable.span.hi() { ordered_viewables } else { remaining_viewables },
523 ))
524 }
525
526 #[inline(always)]
write_coverage_gap<W>(tcx: TyCtxt<'_>, lo: BytePos, hi: BytePos, w: &mut W) -> io::Result<()> where W: Write,527 fn write_coverage_gap<W>(tcx: TyCtxt<'_>, lo: BytePos, hi: BytePos, w: &mut W) -> io::Result<()>
528 where
529 W: Write,
530 {
531 let span = Span::with_root_ctxt(lo, hi);
532 if let Some(ref html_snippet) = make_html_snippet(tcx, span, None) {
533 write_span(html_snippet, "", false, 0, w)
534 } else {
535 Ok(())
536 }
537 }
538
write_span<W>( html_snippet: &str, tooltip: &str, alt: bool, layer: usize, w: &mut W, ) -> io::Result<()> where W: Write,539 fn write_span<W>(
540 html_snippet: &str,
541 tooltip: &str,
542 alt: bool,
543 layer: usize,
544 w: &mut W,
545 ) -> io::Result<()>
546 where
547 W: Write,
548 {
549 let maybe_alt_class = if layer > 0 {
550 if alt { " odd" } else { " even" }
551 } else {
552 ""
553 };
554 let maybe_title_attr = if !tooltip.is_empty() {
555 format!(" title=\"{}\"", escape_attr(tooltip))
556 } else {
557 "".to_owned()
558 };
559 if layer == 1 {
560 write!(w, "<span>")?;
561 }
562 for (i, line) in html_snippet.lines().enumerate() {
563 if i > 0 {
564 write!(w, "{}", NEW_LINE_SPAN)?;
565 }
566 write!(
567 w,
568 r#"<span class="code{}" style="--layer: {}"{}>{}</span>"#,
569 maybe_alt_class, layer, maybe_title_attr, line
570 )?;
571 }
572 // Check for and translate trailing newlines, because `str::lines()` ignores them
573 if html_snippet.ends_with('\n') {
574 write!(w, "{}", NEW_LINE_SPAN)?;
575 }
576 if layer == 1 {
577 write!(w, "</span>")?;
578 }
579 Ok(())
580 }
581
make_html_snippet( tcx: TyCtxt<'_>, span: Span, some_viewable: Option<&SpanViewable>, ) -> Option<String>582 fn make_html_snippet(
583 tcx: TyCtxt<'_>,
584 span: Span,
585 some_viewable: Option<&SpanViewable>,
586 ) -> Option<String> {
587 let source_map = tcx.sess.source_map();
588 let snippet = source_map
589 .span_to_snippet(span)
590 .unwrap_or_else(|err| bug!("span_to_snippet error for span {:?}: {:?}", span, err));
591 let html_snippet = if let Some(viewable) = some_viewable {
592 let is_head = span.lo() == viewable.span.lo();
593 let is_tail = span.hi() == viewable.span.hi();
594 let mut labeled_snippet = if is_head {
595 format!(r#"<span class="annotation">{}{}</span>"#, viewable.id, ANNOTATION_LEFT_BRACKET)
596 } else {
597 "".to_owned()
598 };
599 if span.is_empty() {
600 if is_head && is_tail {
601 labeled_snippet.push(CARET);
602 }
603 } else {
604 labeled_snippet.push_str(&escape_html(&snippet));
605 };
606 if is_tail {
607 labeled_snippet.push_str(&format!(
608 r#"<span class="annotation">{}{}</span>"#,
609 ANNOTATION_RIGHT_BRACKET, viewable.id
610 ));
611 }
612 labeled_snippet
613 } else {
614 escape_html(&snippet)
615 };
616 if html_snippet.is_empty() { None } else { Some(html_snippet) }
617 }
618
tooltip<'tcx>( tcx: TyCtxt<'tcx>, spanview_id: &str, span: Span, statements: Vec<Statement<'tcx>>, terminator: &Option<Terminator<'tcx>>, ) -> String619 fn tooltip<'tcx>(
620 tcx: TyCtxt<'tcx>,
621 spanview_id: &str,
622 span: Span,
623 statements: Vec<Statement<'tcx>>,
624 terminator: &Option<Terminator<'tcx>>,
625 ) -> String {
626 let source_map = tcx.sess.source_map();
627 let mut text = Vec::new();
628 text.push(format!("{}: {}:", spanview_id, &source_map.span_to_embeddable_string(span)));
629 for statement in statements {
630 let source_range = source_range_no_file(tcx, statement.source_info.span);
631 text.push(format!(
632 "\n{}{}: {}: {:?}",
633 TOOLTIP_INDENT,
634 source_range,
635 statement_kind_name(&statement),
636 statement
637 ));
638 }
639 if let Some(term) = terminator {
640 let source_range = source_range_no_file(tcx, term.source_info.span);
641 text.push(format!(
642 "\n{}{}: {}: {:?}",
643 TOOLTIP_INDENT,
644 source_range,
645 terminator_kind_name(term),
646 term.kind
647 ));
648 }
649 text.join("")
650 }
651
trim_span(span: Span, from_pos: BytePos, to_pos: BytePos) -> Span652 fn trim_span(span: Span, from_pos: BytePos, to_pos: BytePos) -> Span {
653 trim_span_hi(trim_span_lo(span, from_pos), to_pos)
654 }
655
trim_span_lo(span: Span, from_pos: BytePos) -> Span656 fn trim_span_lo(span: Span, from_pos: BytePos) -> Span {
657 if from_pos <= span.lo() { span } else { span.with_lo(cmp::min(span.hi(), from_pos)) }
658 }
659
trim_span_hi(span: Span, to_pos: BytePos) -> Span660 fn trim_span_hi(span: Span, to_pos: BytePos) -> Span {
661 if to_pos >= span.hi() { span } else { span.with_hi(cmp::max(span.lo(), to_pos)) }
662 }
663
fn_span(tcx: TyCtxt<'_>, def_id: DefId) -> Span664 fn fn_span(tcx: TyCtxt<'_>, def_id: DefId) -> Span {
665 let fn_decl_span = tcx.def_span(def_id);
666 if let Some(body_span) = hir_body(tcx, def_id).map(|hir_body| hir_body.value.span) {
667 if fn_decl_span.eq_ctxt(body_span) { fn_decl_span.to(body_span) } else { body_span }
668 } else {
669 fn_decl_span
670 }
671 }
672
hir_body(tcx: TyCtxt<'_>, def_id: DefId) -> Option<&rustc_hir::Body<'_>>673 fn hir_body(tcx: TyCtxt<'_>, def_id: DefId) -> Option<&rustc_hir::Body<'_>> {
674 let hir_node = tcx.hir().get_if_local(def_id).expect("expected DefId is local");
675 hir::map::associated_body(hir_node).map(|(_, fn_body_id)| tcx.hir().body(fn_body_id))
676 }
677
escape_html(s: &str) -> String678 fn escape_html(s: &str) -> String {
679 s.replace('&', "&").replace('<', "<").replace('>', ">")
680 }
681
escape_attr(s: &str) -> String682 fn escape_attr(s: &str) -> String {
683 s.replace('&', "&")
684 .replace('\"', """)
685 .replace('\'', "'")
686 .replace('<', "<")
687 .replace('>', ">")
688 }
689