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