Skip to content

Commit ce3a4af

Browse files
committed
Rollup merge of rust-lang#21980 - pnkfelix:more-robust-span-to-snippet, r=huonw
This can be considered partial work on rust-lang#8256. The main observable change: macro expansion sometimes results in spans where `lo > hi`; so for now, when we have such a span, do not attempt to return a snippet result. (Longer term, we might think about whether we could still present a snippet for the cases where this arises, e.g. perhaps by showing the whole macro as the snippet, assuming that is the sole cause of such spans; or by somehow looking up the closest AST node that holds both `lo` and `hi`, and showing that.) As a drive-by, revised the API to return a `Result` rather than an `Option`, with better information-packed error value that should help us (and maybe also our users) identify the causes of such problems in the future. Ideally the call-sites that really want an actual snippet would be updated to catch the newly added `Err` case and print something meaningful about it, but that is not part of this PR.
2 parents 60c9f56 + fa9d223 commit ce3a4af

File tree

5 files changed

+53
-15
lines changed

5 files changed

+53
-15
lines changed

src/librustc_trans/save/span_utils.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ impl<'a> SpanUtils<'a> {
6969

7070
pub fn snippet(&self, span: Span) -> String {
7171
match self.sess.codemap().span_to_snippet(span) {
72-
Some(s) => s,
73-
None => String::new(),
72+
Ok(s) => s,
73+
Err(_) => String::new(),
7474
}
7575
}
7676

src/librustc_trans/trans/debuginfo.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1094,7 +1094,7 @@ pub fn get_cleanup_debug_loc_for_ast_node<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
10941094
// bodies), in which case we also just want to return the span of the
10951095
// whole expression.
10961096
let code_snippet = cx.sess().codemap().span_to_snippet(node_span);
1097-
if let Some(code_snippet) = code_snippet {
1097+
if let Ok(code_snippet) = code_snippet {
10981098
let bytes = code_snippet.as_bytes();
10991099

11001100
if bytes.len() > 0 && &bytes[bytes.len()-1..] == b"}" {

src/librustdoc/clean/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2301,8 +2301,8 @@ impl ToSource for syntax::codemap::Span {
23012301
fn to_src(&self, cx: &DocContext) -> String {
23022302
debug!("converting span {:?} to snippet", self.clean(cx));
23032303
let sn = match cx.sess().codemap().span_to_snippet(*self) {
2304-
Some(x) => x.to_string(),
2305-
None => "".to_string()
2304+
Ok(x) => x.to_string(),
2305+
Err(_) => "".to_string()
23062306
};
23072307
debug!("got snippet {}", sn);
23082308
sn

src/libsyntax/codemap.rs

+46-8
Original file line numberDiff line numberDiff line change
@@ -437,18 +437,35 @@ impl CodeMap {
437437
FileLines {file: lo.file, lines: lines}
438438
}
439439

440-
pub fn span_to_snippet(&self, sp: Span) -> Option<String> {
440+
pub fn span_to_snippet(&self, sp: Span) -> Result<String, SpanSnippetError> {
441+
if sp.lo > sp.hi {
442+
return Err(SpanSnippetError::IllFormedSpan(sp));
443+
}
444+
441445
let begin = self.lookup_byte_offset(sp.lo);
442446
let end = self.lookup_byte_offset(sp.hi);
443447

444-
// FIXME #8256: this used to be an assert but whatever precondition
445-
// it's testing isn't true for all spans in the AST, so to allow the
446-
// caller to not have to panic (and it can't catch it since the CodeMap
447-
// isn't sendable), return None
448448
if begin.fm.start_pos != end.fm.start_pos {
449-
None
449+
return Err(SpanSnippetError::DistinctSources(DistinctSources {
450+
begin: (begin.fm.name.clone(),
451+
begin.fm.start_pos),
452+
end: (end.fm.name.clone(),
453+
end.fm.start_pos)
454+
}));
450455
} else {
451-
Some((&begin.fm.src[begin.pos.to_usize()..end.pos.to_usize()]).to_string())
456+
let start = begin.pos.to_usize();
457+
let limit = end.pos.to_usize();
458+
if start > limit || limit > begin.fm.src.len() {
459+
return Err(SpanSnippetError::MalformedForCodemap(
460+
MalformedCodemapPositions {
461+
name: begin.fm.name.clone(),
462+
source_len: begin.fm.src.len(),
463+
begin_pos: begin.pos,
464+
end_pos: end.pos,
465+
}));
466+
}
467+
468+
return Ok((&begin.fm.src[start..limit]).to_string())
452469
}
453470
}
454471

@@ -622,6 +639,27 @@ impl CodeMap {
622639
}
623640
}
624641

642+
#[derive(Clone, PartialEq, Eq, Debug)]
643+
pub enum SpanSnippetError {
644+
IllFormedSpan(Span),
645+
DistinctSources(DistinctSources),
646+
MalformedForCodemap(MalformedCodemapPositions),
647+
}
648+
649+
#[derive(Clone, PartialEq, Eq, Debug)]
650+
pub struct DistinctSources {
651+
begin: (String, BytePos),
652+
end: (String, BytePos)
653+
}
654+
655+
#[derive(Clone, PartialEq, Eq, Debug)]
656+
pub struct MalformedCodemapPositions {
657+
name: String,
658+
source_len: usize,
659+
begin_pos: BytePos,
660+
end_pos: BytePos
661+
}
662+
625663
#[cfg(test)]
626664
mod test {
627665
use super::*;
@@ -773,7 +811,7 @@ mod test {
773811
let span = Span {lo: BytePos(12), hi: BytePos(23), expn_id: NO_EXPANSION};
774812
let snippet = cm.span_to_snippet(span);
775813

776-
assert_eq!(snippet, Some("second line".to_string()));
814+
assert_eq!(snippet, Ok("second line".to_string()));
777815
}
778816

779817
#[test]

src/libsyntax/parse/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1233,8 +1233,8 @@ mod test {
12331233
let span = tts.iter().rev().next().unwrap().get_span();
12341234

12351235
match sess.span_diagnostic.cm.span_to_snippet(span) {
1236-
Some(s) => assert_eq!(&s[], "{ body }"),
1237-
None => panic!("could not get snippet"),
1236+
Ok(s) => assert_eq!(&s[], "{ body }"),
1237+
Err(_) => panic!("could not get snippet"),
12381238
}
12391239
}
12401240
}

0 commit comments

Comments
 (0)