Skip to content

Commit 28cc0c5

Browse files
committed
Auto merge of #42593 - ibabushkin:on-demand-external-source, r=eddyb
Implement lazy loading of external crates' sources. Fixes #38875 Fixes #38875. This is a follow-up to #42507. When a (now correctly translated) span from an external crate is referenced in a error, warning or info message, we still don't have the source code being referenced. Since stuffing the source in the serialized metadata of an rlib is extremely wasteful, the following scheme has been implemented: * File maps now contain a source hash that gets serialized as well. * When a span is rendered in a message, the source hash in the corresponding file map(s) is used to try and load the source from the corresponding file on disk. If the file is not found or the hashes don't match, the failed attempt is recorded (and not retried). * The machinery fetching source lines from file maps is augmented to use the lazily loaded external source as a secondary fallback for file maps belonging to external crates. This required a small change to the expected stderr of one UI test (it now renders a span, where previously was none). Further work can be done based on this - some of the machinery previously used to hide external spans is possibly obsolete and the hashing code can be reused in different places as well. r? @eddyb
2 parents 78d8416 + bd4fe45 commit 28cc0c5

File tree

14 files changed

+257
-81
lines changed

14 files changed

+257
-81
lines changed

src/Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/librustc/ich/impls_syntax.rs

+4
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,8 @@ impl<'a, 'gcx, 'tcx> HashStable<StableHashingContext<'a, 'gcx, 'tcx>> for FileMa
336336
crate_of_origin,
337337
// Do not hash the source as it is not encoded
338338
src: _,
339+
src_hash,
340+
external_src: _,
339341
start_pos,
340342
end_pos: _,
341343
ref lines,
@@ -350,6 +352,8 @@ impl<'a, 'gcx, 'tcx> HashStable<StableHashingContext<'a, 'gcx, 'tcx>> for FileMa
350352
index: CRATE_DEF_INDEX,
351353
}.hash_stable(hcx, hasher);
352354

355+
src_hash.hash_stable(hcx, hasher);
356+
353357
// We only hash the relative position within this filemap
354358
let lines = lines.borrow();
355359
lines.len().hash_stable(hcx, hasher);

src/librustc_data_structures/stable_hasher.rs

+11
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,17 @@ impl StableHasherResult for [u8; 20] {
7878
}
7979
}
8080

81+
impl StableHasherResult for u128 {
82+
fn finish(mut hasher: StableHasher<Self>) -> Self {
83+
let hash_bytes: &[u8] = hasher.finalize();
84+
assert!(hash_bytes.len() >= mem::size_of::<u128>());
85+
86+
unsafe {
87+
::std::ptr::read_unaligned(hash_bytes.as_ptr() as *const u128)
88+
}
89+
}
90+
}
91+
8192
impl StableHasherResult for u64 {
8293
fn finish(mut hasher: StableHasher<Self>) -> Self {
8394
hasher.state.finalize();

src/librustc_errors/emitter.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use RenderSpan::*;
1717
use snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, StyledString, Style};
1818
use styled_buffer::StyledBuffer;
1919

20+
use std::borrow::Cow;
2021
use std::io::prelude::*;
2122
use std::io;
2223
use std::rc::Rc;
@@ -131,7 +132,7 @@ impl EmitterWriter {
131132
}
132133
}
133134

134-
fn preprocess_annotations(&self, msp: &MultiSpan) -> Vec<FileWithAnnotatedLines> {
135+
fn preprocess_annotations(&mut self, msp: &MultiSpan) -> Vec<FileWithAnnotatedLines> {
135136
fn add_annotation_to_file(file_vec: &mut Vec<FileWithAnnotatedLines>,
136137
file: Rc<FileMap>,
137138
line_index: usize,
@@ -175,6 +176,7 @@ impl EmitterWriter {
175176
if span_label.span == DUMMY_SP {
176177
continue;
177178
}
179+
178180
let lo = cm.lookup_char_pos(span_label.span.lo);
179181
let mut hi = cm.lookup_char_pos(span_label.span.hi);
180182

@@ -890,10 +892,10 @@ impl EmitterWriter {
890892
let mut annotated_files = self.preprocess_annotations(msp);
891893

892894
// Make sure our primary file comes first
893-
let primary_lo = if let (Some(ref cm), Some(ref primary_span)) =
895+
let (primary_lo, cm) = if let (Some(cm), Some(ref primary_span)) =
894896
(self.cm.as_ref(), msp.primary_span().as_ref()) {
895897
if primary_span != &&DUMMY_SP {
896-
cm.lookup_char_pos(primary_span.lo)
898+
(cm.lookup_char_pos(primary_span.lo), cm)
897899
} else {
898900
emit_to_destination(&buffer.render(), level, &mut self.dst)?;
899901
return Ok(());
@@ -911,7 +913,7 @@ impl EmitterWriter {
911913
// Print out the annotate source lines that correspond with the error
912914
for annotated_file in annotated_files {
913915
// we can't annotate anything if the source is unavailable.
914-
if annotated_file.file.src.is_none() {
916+
if !cm.ensure_filemap_source_present(annotated_file.file.clone()) {
915917
continue;
916918
}
917919

@@ -1012,7 +1014,7 @@ impl EmitterWriter {
10121014
} else if line_idx_delta == 2 {
10131015
let unannotated_line = annotated_file.file
10141016
.get_line(annotated_file.lines[line_idx].line_index)
1015-
.unwrap_or("");
1017+
.unwrap_or_else(|| Cow::from(""));
10161018

10171019
let last_buffer_line_num = buffer.num_lines();
10181020

src/librustc_errors/lib.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ use self::Level::*;
3737

3838
use emitter::{Emitter, EmitterWriter};
3939

40+
use std::borrow::Cow;
4041
use std::cell::{RefCell, Cell};
4142
use std::{error, fmt};
4243
use std::rc::Rc;
@@ -49,7 +50,7 @@ pub mod registry;
4950
pub mod styled_buffer;
5051
mod lock;
5152

52-
use syntax_pos::{BytePos, Loc, FileLinesResult, FileName, MultiSpan, Span, NO_EXPANSION};
53+
use syntax_pos::{BytePos, Loc, FileLinesResult, FileMap, FileName, MultiSpan, Span, NO_EXPANSION};
5354

5455
#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)]
5556
pub enum RenderSpan {
@@ -103,6 +104,7 @@ pub trait CodeMapper {
103104
fn span_to_filename(&self, sp: Span) -> FileName;
104105
fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span>;
105106
fn call_span_if_macro(&self, sp: Span) -> Span;
107+
fn ensure_filemap_source_present(&self, file_map: Rc<FileMap>) -> bool;
106108
}
107109

108110
impl CodeSuggestion {
@@ -121,7 +123,7 @@ impl CodeSuggestion {
121123
use syntax_pos::{CharPos, Loc, Pos};
122124

123125
fn push_trailing(buf: &mut String,
124-
line_opt: Option<&str>,
126+
line_opt: Option<&Cow<str>>,
125127
lo: &Loc,
126128
hi_opt: Option<&Loc>) {
127129
let (lo, hi_opt) = (lo.col.to_usize(), hi_opt.map(|hi| hi.col.to_usize()));
@@ -183,13 +185,13 @@ impl CodeSuggestion {
183185
let cur_lo = cm.lookup_char_pos(sp.lo);
184186
for (buf, substitute) in bufs.iter_mut().zip(substitutes) {
185187
if prev_hi.line == cur_lo.line {
186-
push_trailing(buf, prev_line, &prev_hi, Some(&cur_lo));
188+
push_trailing(buf, prev_line.as_ref(), &prev_hi, Some(&cur_lo));
187189
} else {
188-
push_trailing(buf, prev_line, &prev_hi, None);
190+
push_trailing(buf, prev_line.as_ref(), &prev_hi, None);
189191
// push lines between the previous and current span (if any)
190192
for idx in prev_hi.line..(cur_lo.line - 1) {
191193
if let Some(line) = fm.get_line(idx) {
192-
buf.push_str(line);
194+
buf.push_str(line.as_ref());
193195
buf.push('\n');
194196
}
195197
}
@@ -205,7 +207,7 @@ impl CodeSuggestion {
205207
for buf in &mut bufs {
206208
// if the replacement already ends with a newline, don't print the next line
207209
if !buf.ends_with('\n') {
208-
push_trailing(buf, prev_line, &prev_hi, None);
210+
push_trailing(buf, prev_line.as_ref(), &prev_hi, None);
209211
}
210212
// remove trailing newline
211213
buf.pop();

src/librustc_metadata/decoder.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -765,7 +765,7 @@ impl<'a, 'tcx> CrateMetadata {
765765
assert!(!self.is_proc_macro(id));
766766
let ast = self.entry(id).ast.unwrap();
767767
let def_id = self.local_def_id(id);
768-
let body = ast.decode(self).body.decode(self);
768+
let body = ast.decode((self, tcx)).body.decode((self, tcx));
769769
tcx.hir.intern_inlined_body(def_id, body)
770770
}
771771

@@ -1149,6 +1149,7 @@ impl<'a, 'tcx> CrateMetadata {
11491149
// containing the information we need.
11501150
let syntax_pos::FileMap { name,
11511151
name_was_remapped,
1152+
src_hash,
11521153
start_pos,
11531154
end_pos,
11541155
lines,
@@ -1174,6 +1175,7 @@ impl<'a, 'tcx> CrateMetadata {
11741175
let local_version = local_codemap.new_imported_filemap(name,
11751176
name_was_remapped,
11761177
self.cnum.as_u32(),
1178+
src_hash,
11771179
source_length,
11781180
lines,
11791181
multibyte_chars);

src/libsyntax/codemap.rs

+39-46
Original file line numberDiff line numberDiff line change
@@ -158,29 +158,13 @@ impl CodeMap {
158158

159159
/// Creates a new filemap without setting its line information. If you don't
160160
/// intend to set the line information yourself, you should use new_filemap_and_lines.
161-
pub fn new_filemap(&self, filename: FileName, mut src: String) -> Rc<FileMap> {
161+
pub fn new_filemap(&self, filename: FileName, src: String) -> Rc<FileMap> {
162162
let start_pos = self.next_start_pos();
163163
let mut files = self.files.borrow_mut();
164164

165-
// Remove utf-8 BOM if any.
166-
if src.starts_with("\u{feff}") {
167-
src.drain(..3);
168-
}
169-
170-
let end_pos = start_pos + src.len();
171-
172165
let (filename, was_remapped) = self.path_mapping.map_prefix(filename);
173-
174-
let filemap = Rc::new(FileMap {
175-
name: filename,
176-
name_was_remapped: was_remapped,
177-
crate_of_origin: 0,
178-
src: Some(Rc::new(src)),
179-
start_pos: Pos::from_usize(start_pos),
180-
end_pos: Pos::from_usize(end_pos),
181-
lines: RefCell::new(Vec::new()),
182-
multibyte_chars: RefCell::new(Vec::new()),
183-
});
166+
let filemap =
167+
Rc::new(FileMap::new(filename, was_remapped, src, Pos::from_usize(start_pos)));
184168

185169
files.push(filemap.clone());
186170

@@ -210,6 +194,7 @@ impl CodeMap {
210194
filename: FileName,
211195
name_was_remapped: bool,
212196
crate_of_origin: u32,
197+
src_hash: u128,
213198
source_len: usize,
214199
mut file_local_lines: Vec<BytePos>,
215200
mut file_local_multibyte_chars: Vec<MultiByteChar>)
@@ -233,6 +218,8 @@ impl CodeMap {
233218
name_was_remapped: name_was_remapped,
234219
crate_of_origin: crate_of_origin,
235220
src: None,
221+
src_hash: src_hash,
222+
external_src: RefCell::new(ExternalSource::AbsentOk),
236223
start_pos: start_pos,
237224
end_pos: end_pos,
238225
lines: RefCell::new(file_local_lines),
@@ -428,30 +415,31 @@ impl CodeMap {
428415
local_end.fm.start_pos)
429416
}));
430417
} else {
431-
match local_begin.fm.src {
432-
Some(ref src) => {
433-
let start_index = local_begin.pos.to_usize();
434-
let end_index = local_end.pos.to_usize();
435-
let source_len = (local_begin.fm.end_pos -
436-
local_begin.fm.start_pos).to_usize();
437-
438-
if start_index > end_index || end_index > source_len {
439-
return Err(SpanSnippetError::MalformedForCodemap(
440-
MalformedCodemapPositions {
441-
name: local_begin.fm.name.clone(),
442-
source_len: source_len,
443-
begin_pos: local_begin.pos,
444-
end_pos: local_end.pos,
445-
}));
446-
}
447-
448-
return Ok((&src[start_index..end_index]).to_string())
449-
}
450-
None => {
451-
return Err(SpanSnippetError::SourceNotAvailable {
452-
filename: local_begin.fm.name.clone()
453-
});
454-
}
418+
self.ensure_filemap_source_present(local_begin.fm.clone());
419+
420+
let start_index = local_begin.pos.to_usize();
421+
let end_index = local_end.pos.to_usize();
422+
let source_len = (local_begin.fm.end_pos -
423+
local_begin.fm.start_pos).to_usize();
424+
425+
if start_index > end_index || end_index > source_len {
426+
return Err(SpanSnippetError::MalformedForCodemap(
427+
MalformedCodemapPositions {
428+
name: local_begin.fm.name.clone(),
429+
source_len: source_len,
430+
begin_pos: local_begin.pos,
431+
end_pos: local_end.pos,
432+
}));
433+
}
434+
435+
if let Some(ref src) = local_begin.fm.src {
436+
return Ok((&src[start_index..end_index]).to_string());
437+
} else if let Some(src) = local_begin.fm.external_src.borrow().get_source() {
438+
return Ok((&src[start_index..end_index]).to_string());
439+
} else {
440+
return Err(SpanSnippetError::SourceNotAvailable {
441+
filename: local_begin.fm.name.clone()
442+
});
455443
}
456444
}
457445
}
@@ -572,6 +560,10 @@ impl CodeMapper for CodeMap {
572560
}
573561
sp
574562
}
563+
fn ensure_filemap_source_present(&self, file_map: Rc<FileMap>) -> bool {
564+
let src = self.file_loader.read_file(Path::new(&file_map.name)).ok();
565+
return file_map.add_external_src(src)
566+
}
575567
}
576568

577569
#[derive(Clone)]
@@ -617,6 +609,7 @@ impl FilePathMapping {
617609
#[cfg(test)]
618610
mod tests {
619611
use super::*;
612+
use std::borrow::Cow;
620613
use std::rc::Rc;
621614

622615
#[test]
@@ -626,12 +619,12 @@ mod tests {
626619
"first line.\nsecond line".to_string());
627620
fm.next_line(BytePos(0));
628621
// Test we can get lines with partial line info.
629-
assert_eq!(fm.get_line(0), Some("first line."));
622+
assert_eq!(fm.get_line(0), Some(Cow::from("first line.")));
630623
// TESTING BROKEN BEHAVIOR: line break declared before actual line break.
631624
fm.next_line(BytePos(10));
632-
assert_eq!(fm.get_line(1), Some("."));
625+
assert_eq!(fm.get_line(1), Some(Cow::from(".")));
633626
fm.next_line(BytePos(12));
634-
assert_eq!(fm.get_line(2), Some("second line"));
627+
assert_eq!(fm.get_line(2), Some(Cow::from("second line")));
635628
}
636629

637630
#[test]

src/libsyntax/json.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ impl DiagnosticSpanLine {
314314
h_end: usize)
315315
-> DiagnosticSpanLine {
316316
DiagnosticSpanLine {
317-
text: fm.get_line(index).unwrap_or("").to_owned(),
317+
text: fm.get_line(index).map_or(String::new(), |l| l.into_owned()),
318318
highlight_start: h_start,
319319
highlight_end: h_end,
320320
}

src/libsyntax_pos/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ crate-type = ["dylib"]
1010

1111
[dependencies]
1212
serialize = { path = "../libserialize" }
13+
rustc_data_structures = { path = "../librustc_data_structures" }

0 commit comments

Comments
 (0)