//! 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.
///
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) {
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.
}
}
-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.
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(
) -> 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,
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);
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
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;
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]
+ ));
}
}
}
}
/// 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);
}
(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);
}
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!(),
}
#[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);