]> git.proxmox.com Git - rustc.git/blobdiff - src/librustc_errors/lib.rs
New upstream version 1.41.1+dfsg1
[rustc.git] / src / librustc_errors / lib.rs
index 9743cf0d805a3eac513bc6568ad62c7997c0a996..53fc1cb1c95980835bd50f9625958c43623d8736 100644 (file)
@@ -3,7 +3,6 @@
 //! This module contains the code for creating and emitting diagnostics.
 
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
-
 #![feature(crate_visibility_modifier)]
 #![cfg_attr(unix, feature(libc))]
 #![feature(nll)]
 
 pub use emitter::ColorConfig;
 
+use log::debug;
 use Level::*;
 
-use emitter::{Emitter, EmitterWriter, is_case_difference};
+use emitter::{is_case_difference, Emitter, EmitterWriter};
 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 rustc_data_structures::sync::{self, Lock, Lrc};
+use syntax_pos::source_map::SourceMap;
+use syntax_pos::{Loc, MultiSpan, Span};
 
 use std::borrow::Cow;
 use std::cell::Cell;
-use std::{error, fmt};
 use std::panic;
 use std::path::Path;
+use std::{error, fmt};
 
-use termcolor::{ColorSpec, Color};
+use termcolor::{Color, ColorSpec};
 
+pub mod annotate_snippet_emitter_writer;
 mod diagnostic;
 mod diagnostic_builder;
 pub mod emitter;
-pub mod annotate_snippet_emitter_writer;
-mod snippet;
+pub mod json;
+mod lock;
 pub mod registry;
+mod snippet;
 mod styled_buffer;
-mod lock;
+pub use snippet::Style;
+
+pub type PResult<'a, T> = Result<T, DiagnosticBuilder<'a>>;
 
-use syntax_pos::{
-    BytePos,
-    FileLinesResult,
-    FileName,
-    Loc,
-    MultiSpan,
-    SourceFile,
-    Span,
-    SpanSnippetError,
-};
+// `PResult` is used a lot. Make sure it doesn't unintentionally get bigger.
+// (See also the comment on `DiagnosticBuilderInner`.)
+#[cfg(target_arch = "x86_64")]
+rustc_data_structures::static_assert_size!(PResult<'_, bool>, 16);
 
 /// Indicates the confidence in the correctness of a suggestion.
 ///
@@ -144,33 +143,18 @@ 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,
-    ) -> Vec<(String, Vec<SubstitutionPart>, bool)> {
+    pub fn splice_lines(&self, cm: &SourceMap) -> Vec<(String, Vec<SubstitutionPart>, bool)> {
         use syntax_pos::{CharPos, Pos};
 
-        fn push_trailing(buf: &mut String,
-                         line_opt: Option<&Cow<'_, str>>,
-                         lo: &Loc,
-                         hi_opt: Option<&Loc>) {
+        fn push_trailing(
+            buf: &mut String,
+            line_opt: Option<&Cow<'_, str>>,
+            lo: &Loc,
+            hi_opt: Option<&Loc>,
+        ) {
             let (lo, hi_opt) = (lo.col.to_usize(), hi_opt.map(|hi| hi.col.to_usize()));
             if let Some(line) = line_opt {
                 if let Some(lo) = line.char_indices().map(|(i, _)| i).nth(lo) {
@@ -189,100 +173,84 @@ impl CodeSuggestion {
 
         assert!(!self.substitutions.is_empty());
 
-        self.substitutions.iter().cloned().map(|mut substitution| {
-            // Assumption: all spans are in the same file, and all spans
-            // are disjoint. Sort in ascending order.
-            substitution.parts.sort_by_key(|part| part.span.lo());
-
-            // Find the bounding span.
-            let lo = substitution.parts.iter().map(|part| part.span.lo()).min().unwrap();
-            let hi = substitution.parts.iter().map(|part| part.span.hi()).min().unwrap();
-            let bounding_span = Span::with_root_ctxt(lo, hi);
-            let lines = cm.span_to_lines(bounding_span).unwrap();
-            assert!(!lines.lines.is_empty());
-
-            // To build up the result, we do this for each span:
-            // - push the line segment trailing the previous span
-            //   (at the beginning a "phantom" span pointing at the start of the line)
-            // - push lines between the previous and current span (if any)
-            // - if the previous and current span are not on the same line
-            //   push the line segment leading up to the current span
-            // - splice in the span substitution
-            //
-            // Finally push the trailing line segment of the last span
-            let fm = &lines.file;
-            let mut prev_hi = cm.lookup_char_pos(bounding_span.lo());
-            prev_hi.col = CharPos::from_usize(0);
-
-            let mut prev_line = fm.get_line(lines.lines[0].line_index);
-            let mut buf = String::new();
-
-            for part in &substitution.parts {
-                let cur_lo = cm.lookup_char_pos(part.span.lo());
-                if prev_hi.line == cur_lo.line {
-                    push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, Some(&cur_lo));
-                } else {
-                    push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, None);
-                    // push lines between the previous and current span (if any)
-                    for idx in prev_hi.line..(cur_lo.line - 1) {
-                        if let Some(line) = fm.get_line(idx) {
-                            buf.push_str(line.as_ref());
-                            buf.push('\n');
+        self.substitutions
+            .iter()
+            .filter(|subst| {
+                // Suggestions coming from macros can have malformed spans. This is a heavy
+                // handed approach to avoid ICEs by ignoring the suggestion outright.
+                let invalid = subst.parts.iter().any(|item| cm.is_valid_span(item.span).is_err());
+                if invalid {
+                    debug!("splice_lines: suggestion contains an invalid span: {:?}", subst);
+                }
+                !invalid
+            })
+            .cloned()
+            .map(|mut substitution| {
+                // Assumption: all spans are in the same file, and all spans
+                // are disjoint. Sort in ascending order.
+                substitution.parts.sort_by_key(|part| part.span.lo());
+
+                // Find the bounding span.
+                let lo = substitution.parts.iter().map(|part| part.span.lo()).min().unwrap();
+                let hi = substitution.parts.iter().map(|part| part.span.hi()).max().unwrap();
+                let bounding_span = Span::with_root_ctxt(lo, hi);
+                let lines = cm.span_to_lines(bounding_span).unwrap();
+                assert!(!lines.lines.is_empty());
+
+                // To build up the result, we do this for each span:
+                // - push the line segment trailing the previous span
+                //   (at the beginning a "phantom" span pointing at the start of the line)
+                // - push lines between the previous and current span (if any)
+                // - if the previous and current span are not on the same line
+                //   push the line segment leading up to the current span
+                // - splice in the span substitution
+                //
+                // Finally push the trailing line segment of the last span
+                let fm = &lines.file;
+                let mut prev_hi = cm.lookup_char_pos(bounding_span.lo());
+                prev_hi.col = CharPos::from_usize(0);
+
+                let mut prev_line = fm.get_line(lines.lines[0].line_index);
+                let mut buf = String::new();
+
+                for part in &substitution.parts {
+                    let cur_lo = cm.lookup_char_pos(part.span.lo());
+                    if prev_hi.line == cur_lo.line {
+                        push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, Some(&cur_lo));
+                    } else {
+                        push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, None);
+                        // push lines between the previous and current span (if any)
+                        for idx in prev_hi.line..(cur_lo.line - 1) {
+                            if let Some(line) = fm.get_line(idx) {
+                                buf.push_str(line.as_ref());
+                                buf.push('\n');
+                            }
+                        }
+                        if let Some(cur_line) = fm.get_line(cur_lo.line - 1) {
+                            let end = std::cmp::min(cur_line.len(), cur_lo.col.to_usize());
+                            buf.push_str(&cur_line[..end]);
                         }
                     }
-                    if let Some(cur_line) = fm.get_line(cur_lo.line - 1) {
-                        let end = std::cmp::min(cur_line.len(), cur_lo.col.to_usize());
-                        buf.push_str(&cur_line[..end]);
-                    }
+                    buf.push_str(&part.snippet);
+                    prev_hi = cm.lookup_char_pos(part.span.hi());
+                    prev_line = fm.get_line(prev_hi.line - 1);
                 }
-                buf.push_str(&part.snippet);
-                prev_hi = cm.lookup_char_pos(part.span.hi());
-                prev_line = fm.get_line(prev_hi.line - 1);
-            }
-            let only_capitalization = is_case_difference(cm, &buf, bounding_span);
-            // if the replacement already ends with a newline, don't print the next line
-            if !buf.ends_with('\n') {
-                push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, None);
-            }
-            // remove trailing newlines
-            while buf.ends_with('\n') {
-                buf.pop();
-            }
-            (buf, substitution.parts, only_capitalization)
-        }).collect()
-    }
-}
-
-/// 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")
+                let only_capitalization = is_case_difference(cm, &buf, bounding_span);
+                // if the replacement already ends with a newline, don't print the next line
+                if !buf.ends_with('\n') {
+                    push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, None);
+                }
+                // remove trailing newlines
+                while buf.ends_with('\n') {
+                    buf.pop();
+                }
+                (buf, substitution.parts, only_capitalization)
+            })
+            .collect()
     }
 }
 
-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.
@@ -301,7 +269,7 @@ impl error::Error for ExplicitBug {
     }
 }
 
-pub use diagnostic::{Diagnostic, SubDiagnostic, DiagnosticStyledString, DiagnosticId};
+pub use diagnostic::{Diagnostic, DiagnosticId, DiagnosticStyledString, SubDiagnostic};
 pub use diagnostic_builder::DiagnosticBuilder;
 
 /// A handler deals with errors and other compiler output.
@@ -399,22 +367,18 @@ 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,
             cm,
-            HandlerFlags {
-                can_emit_warnings,
-                treat_err_as_bug,
-                .. Default::default()
-            },
+            HandlerFlags { can_emit_warnings, treat_err_as_bug, ..Default::default() },
         )
     }
 
     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(
@@ -435,17 +399,13 @@ impl Handler {
     ) -> Self {
         Handler::with_emitter_and_flags(
             emitter,
-            HandlerFlags {
-                can_emit_warnings,
-                treat_err_as_bug,
-                .. Default::default()
-            },
+            HandlerFlags { can_emit_warnings, treat_err_as_bug, ..Default::default() },
         )
     }
 
     pub fn with_emitter_and_flags(
         emitter: Box<dyn Emitter + sync::Send>,
-        flags: HandlerFlags
+        flags: HandlerFlags,
     ) -> Self {
         Self {
             flags,
@@ -501,7 +461,10 @@ impl Handler {
             old_diag.level = Bug;
             old_diag.note(&format!(
                 "{}:{}: already existing stashed diagnostic with (span = {:?}, key = {:?})",
-                file!(), line!(), span, key
+                file!(),
+                line!(),
+                span,
+                key
             ));
             inner.emit_diag_at_span(old_diag, span);
             panic!(ExplicitBug);
@@ -619,6 +582,11 @@ impl Handler {
         DiagnosticBuilder::new(self, Level::Fatal, msg)
     }
 
+    /// Construct a builder at the `Help` level with the `msg`.
+    pub fn struct_help(&self, msg: &str) -> DiagnosticBuilder<'_> {
+        DiagnosticBuilder::new(self, Level::Help, msg)
+    }
+
     pub fn span_fatal(&self, span: impl Into<MultiSpan>, msg: &str) -> FatalError {
         self.emit_diag_at_span(Diagnostic::new(Fatal, msg), span);
         FatalError
@@ -818,7 +786,7 @@ impl HandlerInner {
         let s = match self.deduplicated_err_count {
             0 => return,
             1 => "aborting due to previous error".to_string(),
-            count => format!("aborting due to {} previous errors", count)
+            count => format!("aborting due to {} previous errors", count),
         };
         if self.treat_err_as_bug() {
             return;
@@ -843,16 +811,22 @@ impl HandlerInner {
                 error_codes.sort();
                 if error_codes.len() > 1 {
                     let limit = if error_codes.len() > 9 { 9 } else { error_codes.len() };
-                    self.failure(&format!("Some errors have detailed explanations: {}{}",
-                                          error_codes[..limit].join(", "),
-                                          if error_codes.len() > 9 { "..." } else { "." }));
-                    self.failure(&format!("For more information about an error, try \
+                    self.failure(&format!(
+                        "Some errors have detailed explanations: {}{}",
+                        error_codes[..limit].join(", "),
+                        if error_codes.len() > 9 { "..." } else { "." }
+                    ));
+                    self.failure(&format!(
+                        "For more information about an error, try \
                                            `rustc --explain {}`.",
-                                          &error_codes[0]));
+                        &error_codes[0]
+                    ));
                 } else {
-                    self.failure(&format!("For more information about this error, try \
+                    self.failure(&format!(
+                        "For more information about this error, try \
                                            `rustc --explain {}`.",
-                                          &error_codes[0]));
+                        &error_codes[0]
+                    ));
                 }
             }
         }
@@ -919,7 +893,7 @@ impl HandlerInner {
     }
 
     /// Emit an error; level should be `Error` or `Fatal`.
-    fn emit_error(&mut self, level: Level, msg: &str,) {
+    fn emit_error(&mut self, level: Level, msg: &str) {
         if self.treat_err_as_bug() {
             self.bug(msg);
         }
@@ -949,13 +923,10 @@ impl HandlerInner {
                 (0, _) => return,
                 (1, 1) => "aborting due to `-Z treat-err-as-bug=1`".to_string(),
                 (1, _) => return,
-                (count, as_bug) => {
-                    format!(
-                        "aborting after {} errors due to `-Z treat-err-as-bug={}`",
-                        count,
-                        as_bug,
-                    )
-                }
+                (count, as_bug) => format!(
+                    "aborting after {} errors due to `-Z treat-err-as-bug={}`",
+                    count, as_bug,
+                ),
             };
             panic!(s);
         }
@@ -985,20 +956,16 @@ impl Level {
         let mut spec = ColorSpec::new();
         match self {
             Bug | Fatal | Error => {
-                spec.set_fg(Some(Color::Red))
-                    .set_intense(true);
+                spec.set_fg(Some(Color::Red)).set_intense(true);
             }
             Warning => {
-                spec.set_fg(Some(Color::Yellow))
-                    .set_intense(cfg!(windows));
+                spec.set_fg(Some(Color::Yellow)).set_intense(cfg!(windows));
             }
             Note => {
-                spec.set_fg(Some(Color::Green))
-                    .set_intense(true);
+                spec.set_fg(Some(Color::Green)).set_intense(true);
             }
             Help => {
-                spec.set_fg(Some(Color::Cyan))
-                    .set_intense(true);
+                spec.set_fg(Some(Color::Cyan)).set_intense(true);
             }
             FailureNote => {}
             Cancelled => unreachable!(),
@@ -1027,8 +994,15 @@ impl Level {
 }
 
 #[macro_export]
-macro_rules! pluralise {
+macro_rules! pluralize {
     ($x:expr) => {
         if $x != 1 { "s" } else { "" }
     };
 }
+
+// Useful type to use with `Result<>` indicate that an error has already
+// been reported to the user, so no need to continue checking.
+#[derive(Clone, Copy, Debug, RustcEncodable, RustcDecodable, Hash, PartialEq, Eq)]
+pub struct ErrorReported;
+
+rustc_data_structures::impl_stable_hash_via_hash!(ErrorReported);