diff options
| author | bors <bors@rust-lang.org> | 2019-11-16 02:40:52 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2019-11-16 02:40:52 +0000 |
| commit | 1d8b6ce89e0874b5e93c9e41bfdd565c56372bb0 (patch) | |
| tree | 363c94e009f1ebf5b5b562c34caac92815a497e4 /src/librustc_errors | |
| parent | 82161cda33406ae8dda08b3e4afe97a44b193792 (diff) | |
| parent | ae9a62633aa0c92f826ffe0d82e9e03b41a66de1 (diff) | |
| download | rust-1d8b6ce89e0874b5e93c9e41bfdd565c56372bb0.tar.gz rust-1d8b6ce89e0874b5e93c9e41bfdd565c56372bb0.zip | |
Auto merge of #66453 - Centril:rollup-w1ohzxs, r=Centril
Rollup of 5 pull requests Successful merges: - #66350 (protect creation of destructors by a mutex) - #66407 (Add more tests for fixed ICEs) - #66415 (Add --force-run-in-process unstable option to libtest) - #66427 (Move the JSON error emitter to librustc_errors) - #66441 (libpanic_unwind for Miri: make sure we have the SEH lang items when needed) Failed merges: r? @ghost
Diffstat (limited to 'src/librustc_errors')
| -rw-r--r-- | src/librustc_errors/annotate_snippet_emitter_writer.rs | 11 | ||||
| -rw-r--r-- | src/librustc_errors/emitter.rs | 25 | ||||
| -rw-r--r-- | src/librustc_errors/json.rs | 434 | ||||
| -rw-r--r-- | src/librustc_errors/json/tests.rs | 205 | ||||
| -rw-r--r-- | src/librustc_errors/lib.rs | 65 |
5 files changed, 665 insertions, 75 deletions
diff --git a/src/librustc_errors/annotate_snippet_emitter_writer.rs b/src/librustc_errors/annotate_snippet_emitter_writer.rs index 491bc2aa6a2..4c5d0178b2c 100644 --- a/src/librustc_errors/annotate_snippet_emitter_writer.rs +++ b/src/librustc_errors/annotate_snippet_emitter_writer.rs @@ -6,9 +6,10 @@ //! [annotate_snippets]: https://docs.rs/crate/annotate-snippets/ use syntax_pos::{SourceFile, MultiSpan, Loc}; +use syntax_pos::source_map::SourceMap; use crate::{ Level, CodeSuggestion, Diagnostic, Emitter, - SourceMapperDyn, SubDiagnostic, DiagnosticId + SubDiagnostic, DiagnosticId }; use crate::emitter::FileWithAnnotatedLines; use rustc_data_structures::sync::Lrc; @@ -20,7 +21,7 @@ use annotate_snippets::formatter::DisplayListFormatter; /// Generates diagnostics using annotate-snippet pub struct AnnotateSnippetEmitterWriter { - source_map: Option<Lrc<SourceMapperDyn>>, + source_map: Option<Lrc<SourceMap>>, /// If true, hides the longer explanation text short_message: bool, /// If true, will normalize line numbers with `LL` to prevent noise in UI test diffs. @@ -49,7 +50,7 @@ impl Emitter for AnnotateSnippetEmitterWriter { &suggestions); } - fn source_map(&self) -> Option<&Lrc<SourceMapperDyn>> { + fn source_map(&self) -> Option<&Lrc<SourceMap>> { self.source_map.as_ref() } @@ -61,7 +62,7 @@ impl Emitter for AnnotateSnippetEmitterWriter { /// Collects all the data needed to generate the data structures needed for the /// `annotate-snippets` library. struct DiagnosticConverter<'a> { - source_map: Option<Lrc<SourceMapperDyn>>, + source_map: Option<Lrc<SourceMap>>, level: Level, message: String, code: Option<DiagnosticId>, @@ -168,7 +169,7 @@ impl<'a> DiagnosticConverter<'a> { impl AnnotateSnippetEmitterWriter { pub fn new( - source_map: Option<Lrc<SourceMapperDyn>>, + source_map: Option<Lrc<SourceMap>>, short_message: bool, external_macro_backtrace: bool, ) -> Self { diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index 291920f17f6..ea779982ba9 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -10,10 +10,11 @@ use Destination::*; use syntax_pos::{SourceFile, Span, MultiSpan}; +use syntax_pos::source_map::SourceMap; use crate::{ Level, CodeSuggestion, Diagnostic, SubDiagnostic, pluralize, - SuggestionStyle, SourceMapper, SourceMapperDyn, DiagnosticId, + SuggestionStyle, DiagnosticId, }; use crate::Level::Error; use crate::snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, StyledString, Style}; @@ -49,7 +50,7 @@ impl HumanReadableErrorType { pub fn new_emitter( self, dst: Box<dyn Write + Send>, - source_map: Option<Lrc<SourceMapperDyn>>, + source_map: Option<Lrc<SourceMap>>, teach: bool, terminal_width: Option<usize>, external_macro_backtrace: bool, @@ -192,7 +193,7 @@ pub trait Emitter { true } - fn source_map(&self) -> Option<&Lrc<SourceMapperDyn>>; + fn source_map(&self) -> Option<&Lrc<SourceMap>>; /// Formats the substitutions of the primary_span /// @@ -271,7 +272,7 @@ pub trait Emitter { // point directly at <*macros>. Since these are often difficult to read, this // will change the span to point at the use site. fn fix_multispans_in_std_macros(&self, - source_map: &Option<Lrc<SourceMapperDyn>>, + source_map: &Option<Lrc<SourceMap>>, span: &mut MultiSpan, children: &mut Vec<SubDiagnostic>, level: &Level, @@ -311,7 +312,7 @@ pub trait Emitter { // <*macros>. Since these locations are often difficult to read, we move these Spans from // <*macros> to their corresponding use site. fn fix_multispan_in_std_macros(&self, - source_map: &Option<Lrc<SourceMapperDyn>>, + source_map: &Option<Lrc<SourceMap>>, span: &mut MultiSpan, always_backtrace: bool) -> bool { let sm = match source_map { @@ -397,7 +398,7 @@ pub trait Emitter { } impl Emitter for EmitterWriter { - fn source_map(&self) -> Option<&Lrc<SourceMapperDyn>> { + fn source_map(&self) -> Option<&Lrc<SourceMap>> { self.sm.as_ref() } @@ -428,7 +429,7 @@ impl Emitter for EmitterWriter { pub struct SilentEmitter; impl Emitter for SilentEmitter { - fn source_map(&self) -> Option<&Lrc<SourceMapperDyn>> { None } + fn source_map(&self) -> Option<&Lrc<SourceMap>> { None } fn emit_diagnostic(&mut self, _: &Diagnostic) {} } @@ -476,7 +477,7 @@ impl ColorConfig { /// Handles the writing of `HumanReadableErrorType::Default` and `HumanReadableErrorType::Short` pub struct EmitterWriter { dst: Destination, - sm: Option<Lrc<SourceMapperDyn>>, + sm: Option<Lrc<SourceMap>>, short_message: bool, teach: bool, ui_testing: bool, @@ -495,7 +496,7 @@ pub struct FileWithAnnotatedLines { impl EmitterWriter { pub fn stderr( color_config: ColorConfig, - source_map: Option<Lrc<SourceMapperDyn>>, + source_map: Option<Lrc<SourceMap>>, short_message: bool, teach: bool, terminal_width: Option<usize>, @@ -515,7 +516,7 @@ impl EmitterWriter { pub fn new( dst: Box<dyn Write + Send>, - source_map: Option<Lrc<SourceMapperDyn>>, + source_map: Option<Lrc<SourceMap>>, short_message: bool, teach: bool, colored: bool, @@ -1685,7 +1686,7 @@ impl FileWithAnnotatedLines { /// This helps us quickly iterate over the whole message (including secondary file spans) pub fn collect_annotations( msp: &MultiSpan, - source_map: &Option<Lrc<SourceMapperDyn>> + source_map: &Option<Lrc<SourceMap>> ) -> Vec<FileWithAnnotatedLines> { fn add_annotation_to_file(file_vec: &mut Vec<FileWithAnnotatedLines>, file: Lrc<SourceFile>, @@ -2067,7 +2068,7 @@ impl<'a> Drop for WritableDst<'a> { } /// Whether the original and suggested code are visually similar enough to warrant extra wording. -pub fn is_case_difference(sm: &dyn SourceMapper, suggested: &str, sp: Span) -> bool { +pub fn is_case_difference(sm: &SourceMap, suggested: &str, sp: Span) -> bool { // FIXME: this should probably be extended to also account for `FO0` → `FOO` and unicode. let found = sm.span_to_snippet(sp).unwrap(); let ascii_confusables = &['c', 'f', 'i', 'k', 'o', 's', 'u', 'v', 'w', 'x', 'y', 'z']; diff --git a/src/librustc_errors/json.rs b/src/librustc_errors/json.rs new file mode 100644 index 00000000000..ebbd49bd84a --- /dev/null +++ b/src/librustc_errors/json.rs @@ -0,0 +1,434 @@ +//! A JSON emitter for errors. +//! +//! This works by converting errors to a simplified structural format (see the +//! structs at the start of the file) and then serializing them. These should +//! contain as much information about the error as possible. +//! +//! The format of the JSON output should be considered *unstable*. For now the +//! structs at the end of this file (Diagnostic*) specify the error format. + +// FIXME: spec the JSON output properly. + +use syntax_pos::source_map::{SourceMap, FilePathMapping}; + +use crate::registry::Registry; +use crate::{SubDiagnostic, CodeSuggestion}; +use crate::{DiagnosticId, Applicability}; +use crate::emitter::{Emitter, HumanReadableErrorType}; + +use syntax_pos::{MacroBacktrace, Span, SpanLabel, MultiSpan}; +use rustc_data_structures::sync::Lrc; +use std::io::{self, Write}; +use std::path::Path; +use std::vec; +use std::sync::{Arc, Mutex}; + +use rustc_serialize::json::{as_json, as_pretty_json}; + +#[cfg(test)] +mod tests; + +pub struct JsonEmitter { + dst: Box<dyn Write + Send>, + registry: Option<Registry>, + sm: Lrc<SourceMap>, + pretty: bool, + ui_testing: bool, + json_rendered: HumanReadableErrorType, + external_macro_backtrace: bool, +} + +impl JsonEmitter { + pub fn stderr( + registry: Option<Registry>, + source_map: Lrc<SourceMap>, + pretty: bool, + json_rendered: HumanReadableErrorType, + external_macro_backtrace: bool, + ) -> JsonEmitter { + JsonEmitter { + dst: Box::new(io::stderr()), + registry, + sm: source_map, + pretty, + ui_testing: false, + json_rendered, + external_macro_backtrace, + } + } + + pub fn basic( + pretty: bool, + json_rendered: HumanReadableErrorType, + external_macro_backtrace: bool, + ) -> JsonEmitter { + let file_path_mapping = FilePathMapping::empty(); + JsonEmitter::stderr(None, Lrc::new(SourceMap::new(file_path_mapping)), + pretty, json_rendered, external_macro_backtrace) + } + + pub fn new( + dst: Box<dyn Write + Send>, + registry: Option<Registry>, + source_map: Lrc<SourceMap>, + pretty: bool, + json_rendered: HumanReadableErrorType, + external_macro_backtrace: bool, + ) -> JsonEmitter { + JsonEmitter { + dst, + registry, + sm: source_map, + pretty, + ui_testing: false, + json_rendered, + external_macro_backtrace, + } + } + + pub fn ui_testing(self, ui_testing: bool) -> Self { + Self { ui_testing, ..self } + } +} + +impl Emitter for JsonEmitter { + fn emit_diagnostic(&mut self, diag: &crate::Diagnostic) { + let data = Diagnostic::from_errors_diagnostic(diag, self); + let result = if self.pretty { + writeln!(&mut self.dst, "{}", as_pretty_json(&data)) + } else { + writeln!(&mut self.dst, "{}", as_json(&data)) + }; + if let Err(e) = result { + panic!("failed to print diagnostics: {:?}", e); + } + } + + fn emit_artifact_notification(&mut self, path: &Path, artifact_type: &str) { + let data = ArtifactNotification { artifact: path, emit: artifact_type }; + let result = if self.pretty { + writeln!(&mut self.dst, "{}", as_pretty_json(&data)) + } else { + writeln!(&mut self.dst, "{}", as_json(&data)) + }; + if let Err(e) = result { + panic!("failed to print notification: {:?}", e); + } + } + + fn source_map(&self) -> Option<&Lrc<SourceMap>> { + Some(&self.sm) + } + + fn should_show_explain(&self) -> bool { + match self.json_rendered { + HumanReadableErrorType::Short(_) => false, + _ => true, + } + } +} + +// The following data types are provided just for serialisation. + +#[derive(RustcEncodable)] +struct Diagnostic { + /// The primary error message. + message: String, + code: Option<DiagnosticCode>, + /// "error: internal compiler error", "error", "warning", "note", "help". + level: &'static str, + spans: Vec<DiagnosticSpan>, + /// Associated diagnostic messages. + children: Vec<Diagnostic>, + /// The message as rustc would render it. + rendered: Option<String>, +} + +#[derive(RustcEncodable)] +struct DiagnosticSpan { + file_name: String, + byte_start: u32, + byte_end: u32, + /// 1-based. + line_start: usize, + line_end: usize, + /// 1-based, character offset. + column_start: usize, + column_end: usize, + /// Is this a "primary" span -- meaning the point, or one of the points, + /// where the error occurred? + is_primary: bool, + /// Source text from the start of line_start to the end of line_end. + text: Vec<DiagnosticSpanLine>, + /// Label that should be placed at this location (if any) + label: Option<String>, + /// If we are suggesting a replacement, this will contain text + /// that should be sliced in atop this span. + suggested_replacement: Option<String>, + /// If the suggestion is approximate + suggestion_applicability: Option<Applicability>, + /// Macro invocations that created the code at this span, if any. + expansion: Option<Box<DiagnosticSpanMacroExpansion>>, +} + +#[derive(RustcEncodable)] +struct DiagnosticSpanLine { + text: String, + + /// 1-based, character offset in self.text. + highlight_start: usize, + + highlight_end: usize, +} + +#[derive(RustcEncodable)] +struct DiagnosticSpanMacroExpansion { + /// span where macro was applied to generate this code; note that + /// this may itself derive from a macro (if + /// `span.expansion.is_some()`) + span: DiagnosticSpan, + + /// name of macro that was applied (e.g., "foo!" or "#[derive(Eq)]") + macro_decl_name: String, + + /// span where macro was defined (if known) + def_site_span: DiagnosticSpan, +} + +#[derive(RustcEncodable)] +struct DiagnosticCode { + /// The code itself. + code: String, + /// An explanation for the code. + explanation: Option<&'static str>, +} + +#[derive(RustcEncodable)] +struct ArtifactNotification<'a> { + /// The path of the artifact. + artifact: &'a Path, + /// What kind of artifact we're emitting. + emit: &'a str, +} + +impl Diagnostic { + fn from_errors_diagnostic(diag: &crate::Diagnostic, + je: &JsonEmitter) + -> Diagnostic { + let sugg = diag.suggestions.iter().map(|sugg| { + Diagnostic { + message: sugg.msg.clone(), + code: None, + level: "help", + spans: DiagnosticSpan::from_suggestion(sugg, je), + children: vec![], + rendered: None, + } + }); + + // generate regular command line output and store it in the json + + // A threadsafe buffer for writing. + #[derive(Default, Clone)] + struct BufWriter(Arc<Mutex<Vec<u8>>>); + + impl Write for BufWriter { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.0.lock().unwrap().write(buf) + } + fn flush(&mut self) -> io::Result<()> { + self.0.lock().unwrap().flush() + } + } + let buf = BufWriter::default(); + let output = buf.clone(); + je.json_rendered.new_emitter( + Box::new(buf), Some(je.sm.clone()), false, None, je.external_macro_backtrace + ).ui_testing(je.ui_testing).emit_diagnostic(diag); + let output = Arc::try_unwrap(output.0).unwrap().into_inner().unwrap(); + let output = String::from_utf8(output).unwrap(); + + Diagnostic { + message: diag.message(), + code: DiagnosticCode::map_opt_string(diag.code.clone(), je), + level: diag.level.to_str(), + spans: DiagnosticSpan::from_multispan(&diag.span, je), + children: diag.children.iter().map(|c| { + Diagnostic::from_sub_diagnostic(c, je) + }).chain(sugg).collect(), + rendered: Some(output), + } + } + + fn from_sub_diagnostic(diag: &SubDiagnostic, je: &JsonEmitter) -> Diagnostic { + Diagnostic { + message: diag.message(), + code: None, + level: diag.level.to_str(), + spans: diag.render_span.as_ref() + .map(|sp| DiagnosticSpan::from_multispan(sp, je)) + .unwrap_or_else(|| DiagnosticSpan::from_multispan(&diag.span, je)), + children: vec![], + rendered: None, + } + } +} + +impl DiagnosticSpan { + fn from_span_label(span: SpanLabel, + suggestion: Option<(&String, Applicability)>, + je: &JsonEmitter) + -> DiagnosticSpan { + Self::from_span_etc(span.span, + span.is_primary, + span.label, + suggestion, + je) + } + + fn from_span_etc(span: Span, + is_primary: bool, + label: Option<String>, + suggestion: Option<(&String, Applicability)>, + je: &JsonEmitter) + -> DiagnosticSpan { + // obtain the full backtrace from the `macro_backtrace` + // helper; in some ways, it'd be better to expand the + // backtrace ourselves, but the `macro_backtrace` helper makes + // some decision, such as dropping some frames, and I don't + // want to duplicate that logic here. + let backtrace = span.macro_backtrace().into_iter(); + DiagnosticSpan::from_span_full(span, + is_primary, + label, + suggestion, + backtrace, + je) + } + + fn from_span_full(span: Span, + is_primary: bool, + label: Option<String>, + suggestion: Option<(&String, Applicability)>, + mut backtrace: vec::IntoIter<MacroBacktrace>, + je: &JsonEmitter) + -> DiagnosticSpan { + let start = je.sm.lookup_char_pos(span.lo()); + let end = je.sm.lookup_char_pos(span.hi()); + let backtrace_step = backtrace.next().map(|bt| { + let call_site = + Self::from_span_full(bt.call_site, + false, + None, + None, + backtrace, + je); + let def_site_span = + Self::from_span_full(bt.def_site_span, + false, + None, + None, + vec![].into_iter(), + je); + Box::new(DiagnosticSpanMacroExpansion { + span: call_site, + macro_decl_name: bt.macro_decl_name, + def_site_span, + }) + }); + + DiagnosticSpan { + file_name: start.file.name.to_string(), + byte_start: start.file.original_relative_byte_pos(span.lo()).0, + byte_end: start.file.original_relative_byte_pos(span.hi()).0, + line_start: start.line, + line_end: end.line, + column_start: start.col.0 + 1, + column_end: end.col.0 + 1, + is_primary, + text: DiagnosticSpanLine::from_span(span, je), + suggested_replacement: suggestion.map(|x| x.0.clone()), + suggestion_applicability: suggestion.map(|x| x.1), + expansion: backtrace_step, + label, + } + } + + fn from_multispan(msp: &MultiSpan, je: &JsonEmitter) -> Vec<DiagnosticSpan> { + msp.span_labels() + .into_iter() + .map(|span_str| Self::from_span_label(span_str, None, je)) + .collect() + } + + fn from_suggestion(suggestion: &CodeSuggestion, je: &JsonEmitter) + -> Vec<DiagnosticSpan> { + suggestion.substitutions + .iter() + .flat_map(|substitution| { + substitution.parts.iter().map(move |suggestion_inner| { + let span_label = SpanLabel { + span: suggestion_inner.span, + is_primary: true, + label: None, + }; + DiagnosticSpan::from_span_label(span_label, + Some((&suggestion_inner.snippet, + suggestion.applicability)), + je) + }) + }) + .collect() + } +} + +impl DiagnosticSpanLine { + fn line_from_source_file(fm: &syntax_pos::SourceFile, + index: usize, + h_start: usize, + h_end: usize) + -> DiagnosticSpanLine { + DiagnosticSpanLine { + text: fm.get_line(index).map_or(String::new(), |l| l.into_owned()), + highlight_start: h_start, + highlight_end: h_end, + } + } + + /// Creates a list of DiagnosticSpanLines from span - each line with any part + /// of `span` gets a DiagnosticSpanLine, with the highlight indicating the + /// `span` within the line. + fn from_span(span: Span, je: &JsonEmitter) -> Vec<DiagnosticSpanLine> { + je.sm.span_to_lines(span) + .map(|lines| { + let fm = &*lines.file; + lines.lines + .iter() + .map(|line| DiagnosticSpanLine::line_from_source_file( + fm, + line.line_index, + line.start_col.0 + 1, + line.end_col.0 + 1, + )).collect() + }).unwrap_or_else(|_| vec![]) + } +} + +impl DiagnosticCode { + fn map_opt_string(s: Option<DiagnosticId>, je: &JsonEmitter) -> Option<DiagnosticCode> { + s.map(|s| { + let s = match s { + DiagnosticId::Error(s) => s, + DiagnosticId::Lint(s) => s, + }; + let explanation = je.registry + .as_ref() + .and_then(|registry| registry.find_description(&s)); + + DiagnosticCode { + code: s, + explanation, + } + }) + } +} diff --git a/src/librustc_errors/json/tests.rs b/src/librustc_errors/json/tests.rs new file mode 100644 index 00000000000..4ab5cd21b0b --- /dev/null +++ b/src/librustc_errors/json/tests.rs @@ -0,0 +1,205 @@ +use super::*; + +use crate::json::JsonEmitter; +use syntax_pos::source_map::{FilePathMapping, SourceMap}; + +use crate::emitter::{ColorConfig, HumanReadableErrorType}; +use crate::Handler; +use rustc_serialize::json::decode; +use syntax_pos::{BytePos, Span}; + +use std::str; + +#[derive(RustcDecodable, Debug, PartialEq, Eq)] +struct TestData { + spans: Vec<SpanTestData>, +} + +#[derive(RustcDecodable, Debug, PartialEq, Eq)] +struct SpanTestData { + pub byte_start: u32, + pub byte_end: u32, + pub line_start: u32, + pub column_start: u32, + pub line_end: u32, + pub column_end: u32, +} + +struct Shared<T> { + data: Arc<Mutex<T>>, +} + +impl<T: Write> Write for Shared<T> { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.data.lock().unwrap().write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.data.lock().unwrap().flush() + } +} + +fn with_default_globals(f: impl FnOnce()) { + let globals = syntax_pos::Globals::new(syntax_pos::edition::DEFAULT_EDITION); + syntax_pos::GLOBALS.set(&globals, || { + syntax_pos::GLOBALS.set(&globals, f) + }) +} + +/// Test the span yields correct positions in JSON. +fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) { + let expected_output = TestData { spans: vec![expected_output] }; + + with_default_globals(|| { + let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + sm.new_source_file(Path::new("test.rs").to_owned().into(), code.to_owned()); + + let output = Arc::new(Mutex::new(Vec::new())); + let je = JsonEmitter::new( + Box::new(Shared { data: output.clone() }), + None, + sm, + true, + HumanReadableErrorType::Short(ColorConfig::Never), + false, + ); + + let span = Span::with_root_ctxt(BytePos(span.0), BytePos(span.1)); + let handler = Handler::with_emitter(true, None, Box::new(je)); + handler.span_err(span, "foo"); + + let bytes = output.lock().unwrap(); + let actual_output = str::from_utf8(&bytes).unwrap(); + let actual_output: TestData = decode(actual_output).unwrap(); + + assert_eq!(expected_output, actual_output) + }) +} + +#[test] +fn empty() { + test_positions( + " ", + (0, 1), + SpanTestData { + byte_start: 0, + byte_end: 1, + line_start: 1, + column_start: 1, + line_end: 1, + column_end: 2, + }, + ) +} + +#[test] +fn bom() { + test_positions( + "\u{feff} ", + (0, 1), + SpanTestData { + byte_start: 3, + byte_end: 4, + line_start: 1, + column_start: 1, + line_end: 1, + column_end: 2, + }, + ) +} + +#[test] +fn lf_newlines() { + test_positions( + "\nmod foo;\nmod bar;\n", + (5, 12), + SpanTestData { + byte_start: 5, + byte_end: 12, + line_start: 2, + column_start: 5, + line_end: 3, + column_end: 3, + }, + ) +} + +#[test] +fn crlf_newlines() { + test_positions( + "\r\nmod foo;\r\nmod bar;\r\n", + (5, 12), + SpanTestData { + byte_start: 6, + byte_end: 14, + line_start: 2, + column_start: 5, + line_end: 3, + column_end: 3, + }, + ) +} + +#[test] +fn crlf_newlines_with_bom() { + test_positions( + "\u{feff}\r\nmod foo;\r\nmod bar;\r\n", + (5, 12), + SpanTestData { + byte_start: 9, + byte_end: 17, + line_start: 2, + column_start: 5, + line_end: 3, + column_end: 3, + }, + ) +} + +#[test] +fn span_before_crlf() { + test_positions( + "foo\r\nbar", + (2, 3), + SpanTestData { + byte_start: 2, + byte_end: 3, + line_start: 1, + column_start: 3, + line_end: 1, + column_end: 4, + }, + ) +} + +#[test] +fn span_on_crlf() { + test_positions( + "foo\r\nbar", + (3, 4), + SpanTestData { + byte_start: 3, + byte_end: 5, + line_start: 1, + column_start: 4, + line_end: 2, + column_end: 1, + }, + ) +} + +#[test] +fn span_after_crlf() { + test_positions( + "foo\r\nbar", + (4, 5), + SpanTestData { + byte_start: 5, + byte_end: 6, + line_start: 2, + column_start: 1, + line_end: 2, + column_end: 2, + }, + ) +} diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs index 8ee28875c62..17765ef9dee 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs @@ -18,6 +18,8 @@ use registry::Registry; use rustc_data_structures::sync::{self, Lrc, Lock}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::stable_hasher::StableHasher; +use syntax_pos::source_map::SourceMap; +use syntax_pos::{Loc, Span, MultiSpan}; use std::borrow::Cow; use std::cell::Cell; @@ -35,17 +37,7 @@ mod snippet; pub mod registry; mod styled_buffer; mod lock; - -use syntax_pos::{ - BytePos, - FileLinesResult, - FileName, - Loc, - MultiSpan, - SourceFile, - Span, - SpanSnippetError, -}; +pub mod json; pub type PResult<'a, T> = Result<T, DiagnosticBuilder<'a>>; @@ -150,26 +142,12 @@ pub struct SubstitutionPart { pub snippet: String, } -pub type SourceMapperDyn = dyn SourceMapper + sync::Send + sync::Sync; - -pub trait SourceMapper { - fn lookup_char_pos(&self, pos: BytePos) -> Loc; - fn span_to_lines(&self, sp: Span) -> FileLinesResult; - fn span_to_string(&self, sp: Span) -> String; - fn span_to_snippet(&self, sp: Span) -> Result<String, SpanSnippetError>; - fn span_to_filename(&self, sp: Span) -> FileName; - fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span>; - fn call_span_if_macro(&self, sp: Span) -> Span; - fn ensure_source_file_source_present(&self, source_file: Lrc<SourceFile>) -> bool; - fn doctest_offset_line(&self, file: &FileName, line: usize) -> usize; -} - impl CodeSuggestion { /// Returns the assembled code suggestions, whether they should be shown with an underline /// and whether the substitution only differs in capitalization. pub fn splice_lines( &self, - cm: &SourceMapperDyn, + cm: &SourceMap, ) -> Vec<(String, Vec<SubstitutionPart>, bool)> { use syntax_pos::{CharPos, Pos}; @@ -259,36 +237,7 @@ impl CodeSuggestion { } } -/// Used as a return value to signify a fatal error occurred. (It is also -/// used as the argument to panic at the moment, but that will eventually -/// not be true.) -#[derive(Copy, Clone, Debug)] -#[must_use] -pub struct FatalError; - -pub struct FatalErrorMarker; - -// Don't implement Send on FatalError. This makes it impossible to panic!(FatalError). -// We don't want to invoke the panic handler and print a backtrace for fatal errors. -impl !Send for FatalError {} - -impl FatalError { - pub fn raise(self) -> ! { - panic::resume_unwind(Box::new(FatalErrorMarker)) - } -} - -impl fmt::Display for FatalError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "parser fatal error") - } -} - -impl error::Error for FatalError { - fn description(&self) -> &str { - "The parser has encountered a fatal error" - } -} +pub use syntax_pos::fatal_error::{FatalError, FatalErrorMarker}; /// Signifies that the compiler died with an explicit call to `.bug` /// or `.span_bug` rather than a failed assertion, etc. @@ -405,7 +354,7 @@ impl Handler { color_config: ColorConfig, can_emit_warnings: bool, treat_err_as_bug: Option<usize>, - cm: Option<Lrc<SourceMapperDyn>>, + cm: Option<Lrc<SourceMap>>, ) -> Self { Self::with_tty_emitter_and_flags( color_config, @@ -420,7 +369,7 @@ impl Handler { pub fn with_tty_emitter_and_flags( color_config: ColorConfig, - cm: Option<Lrc<SourceMapperDyn>>, + cm: Option<Lrc<SourceMap>>, flags: HandlerFlags, ) -> Self { let emitter = Box::new(EmitterWriter::stderr( |
