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