1 // Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 #![crate_name = "rustc_errors"]
12 #![unstable(feature = "rustc_private", issue = "27812")]
13 #![crate_type = "dylib"]
14 #![crate_type = "rlib"]
15 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
16 html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
17 html_root_url = "https://doc.rust-lang.org/nightly/")]
20 #![feature(custom_attribute)]
21 #![allow(unused_attributes)]
22 #![feature(rustc_private)]
23 #![feature(staged_api)]
24 #![feature(range_contains)]
26 #![feature(conservative_impl_trait)]
30 extern crate serialize as rustc_serialize;
31 extern crate syntax_pos;
33 pub use emitter::ColorConfig;
37 use emitter::{Emitter, EmitterWriter};
39 use std::cell::{RefCell, Cell};
40 use std::{error, fmt};
44 pub mod diagnostic_builder;
48 pub mod styled_buffer;
51 use syntax_pos::{BytePos, Loc, FileLinesResult, FileName, MultiSpan, Span, NO_EXPANSION};
53 #[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)]
55 /// A FullSpan renders with both with an initial line for the
56 /// message, prefixed by file:linenum, followed by a summary of
57 /// the source code covered by the span.
60 /// A suggestion renders with both with an initial line for the
61 /// message, prefixed by file:linenum, followed by a summary
62 /// of hypothetical source code, where each `String` is spliced
63 /// into the lines in place of the code covered by each span.
64 Suggestion(CodeSuggestion),
67 #[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)]
68 pub struct CodeSuggestion {
69 /// Each substitute can have multiple variants due to multiple
70 /// applicable suggestions
72 /// `foo.bar` might be replaced with `a.b` or `x.y` by replacing
73 /// `foo` and `bar` on their own:
77 /// (0..3, vec!["a", "x"]),
78 /// (4..7, vec!["b", "y"]),
82 /// or by replacing the entire span:
85 /// vec![(0..7, vec!["a.b", "x.y"])]
87 pub substitution_parts: Vec<Substitution>,
91 #[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)]
92 /// See the docs on `CodeSuggestion::substitutions`
93 pub struct Substitution {
95 pub substitutions: Vec<String>,
98 pub trait CodeMapper {
99 fn lookup_char_pos(&self, pos: BytePos) -> Loc;
100 fn span_to_lines(&self, sp: Span) -> FileLinesResult;
101 fn span_to_string(&self, sp: Span) -> String;
102 fn span_to_filename(&self, sp: Span) -> FileName;
103 fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span>;
106 impl CodeSuggestion {
107 /// Returns the number of substitutions
108 fn substitutions(&self) -> usize {
109 self.substitution_parts[0].substitutions.len()
112 /// Returns the number of substitutions
113 pub fn substitution_spans<'a>(&'a self) -> impl Iterator<Item = Span> + 'a {
114 self.substitution_parts.iter().map(|sub| sub.span)
117 /// Returns the assembled code suggestions.
118 pub fn splice_lines(&self, cm: &CodeMapper) -> Vec<String> {
119 use syntax_pos::{CharPos, Loc, Pos};
121 fn push_trailing(buf: &mut String,
122 line_opt: Option<&str>,
124 hi_opt: Option<&Loc>) {
125 let (lo, hi_opt) = (lo.col.to_usize(), hi_opt.map(|hi| hi.col.to_usize()));
126 if let Some(line) = line_opt {
127 if let Some(lo) = line.char_indices().map(|(i, _)| i).nth(lo) {
128 let hi_opt = hi_opt.and_then(|hi| line.char_indices().map(|(i, _)| i).nth(hi));
129 buf.push_str(match hi_opt {
130 Some(hi) => &line[lo..hi],
134 if let None = hi_opt {
140 if self.substitution_parts.is_empty() {
141 return vec![String::new()];
144 let mut primary_spans: Vec<_> = self.substitution_parts
146 .map(|sub| (sub.span, &sub.substitutions))
149 // Assumption: all spans are in the same file, and all spans
150 // are disjoint. Sort in ascending order.
151 primary_spans.sort_by_key(|sp| sp.0.lo);
153 // Find the bounding span.
154 let lo = primary_spans.iter().map(|sp| sp.0.lo).min().unwrap();
155 let hi = primary_spans.iter().map(|sp| sp.0.hi).min().unwrap();
156 let bounding_span = Span {
161 let lines = cm.span_to_lines(bounding_span).unwrap();
162 assert!(!lines.lines.is_empty());
164 // To build up the result, we do this for each span:
165 // - push the line segment trailing the previous span
166 // (at the beginning a "phantom" span pointing at the start of the line)
167 // - push lines between the previous and current span (if any)
168 // - if the previous and current span are not on the same line
169 // push the line segment leading up to the current span
170 // - splice in the span substitution
172 // Finally push the trailing line segment of the last span
173 let fm = &lines.file;
174 let mut prev_hi = cm.lookup_char_pos(bounding_span.lo);
175 prev_hi.col = CharPos::from_usize(0);
177 let mut prev_line = fm.get_line(lines.lines[0].line_index);
178 let mut bufs = vec![String::new(); self.substitutions()];
180 for (sp, substitutes) in primary_spans {
181 let cur_lo = cm.lookup_char_pos(sp.lo);
182 for (buf, substitute) in bufs.iter_mut().zip(substitutes) {
183 if prev_hi.line == cur_lo.line {
184 push_trailing(buf, prev_line, &prev_hi, Some(&cur_lo));
186 push_trailing(buf, prev_line, &prev_hi, None);
187 // push lines between the previous and current span (if any)
188 for idx in prev_hi.line..(cur_lo.line - 1) {
189 if let Some(line) = fm.get_line(idx) {
194 if let Some(cur_line) = fm.get_line(cur_lo.line - 1) {
195 buf.push_str(&cur_line[..cur_lo.col.to_usize()]);
198 buf.push_str(substitute);
200 prev_hi = cm.lookup_char_pos(sp.hi);
201 prev_line = fm.get_line(prev_hi.line - 1);
203 for buf in &mut bufs {
204 // if the replacement already ends with a newline, don't print the next line
205 if !buf.ends_with('\n') {
206 push_trailing(buf, prev_line, &prev_hi, None);
208 // remove trailing newline
215 /// Used as a return value to signify a fatal error occurred. (It is also
216 /// used as the argument to panic at the moment, but that will eventually
218 #[derive(Copy, Clone, Debug)]
220 pub struct FatalError;
222 impl fmt::Display for FatalError {
223 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
224 write!(f, "parser fatal error")
228 impl error::Error for FatalError {
229 fn description(&self) -> &str {
230 "The parser has encountered a fatal error"
234 /// Signifies that the compiler died with an explicit call to `.bug`
235 /// or `.span_bug` rather than a failed assertion, etc.
236 #[derive(Copy, Clone, Debug)]
237 pub struct ExplicitBug;
239 impl fmt::Display for ExplicitBug {
240 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
241 write!(f, "parser internal bug")
245 impl error::Error for ExplicitBug {
246 fn description(&self) -> &str {
247 "The parser has encountered an internal bug"
251 pub use diagnostic::{Diagnostic, SubDiagnostic, DiagnosticStyledString, StringPart};
252 pub use diagnostic_builder::DiagnosticBuilder;
254 /// A handler deals with errors; certain errors
255 /// (fatal, bug, unimpl) may cause immediate exit,
256 /// others log errors for later reporting.
258 err_count: Cell<usize>,
259 emitter: RefCell<Box<Emitter>>,
260 pub can_emit_warnings: bool,
261 treat_err_as_bug: bool,
262 continue_after_error: Cell<bool>,
263 delayed_span_bug: RefCell<Option<(MultiSpan, String)>>,
267 pub fn with_tty_emitter(color_config: ColorConfig,
268 can_emit_warnings: bool,
269 treat_err_as_bug: bool,
270 cm: Option<Rc<CodeMapper>>)
272 let emitter = Box::new(EmitterWriter::stderr(color_config, cm));
273 Handler::with_emitter(can_emit_warnings, treat_err_as_bug, emitter)
276 pub fn with_emitter(can_emit_warnings: bool,
277 treat_err_as_bug: bool,
281 err_count: Cell::new(0),
282 emitter: RefCell::new(e),
283 can_emit_warnings: can_emit_warnings,
284 treat_err_as_bug: treat_err_as_bug,
285 continue_after_error: Cell::new(true),
286 delayed_span_bug: RefCell::new(None),
290 pub fn set_continue_after_error(&self, continue_after_error: bool) {
291 self.continue_after_error.set(continue_after_error);
294 pub fn struct_dummy<'a>(&'a self) -> DiagnosticBuilder<'a> {
295 DiagnosticBuilder::new(self, Level::Cancelled, "")
298 pub fn struct_span_warn<'a, S: Into<MultiSpan>>(&'a self,
301 -> DiagnosticBuilder<'a> {
302 let mut result = DiagnosticBuilder::new(self, Level::Warning, msg);
304 if !self.can_emit_warnings {
309 pub fn struct_span_warn_with_code<'a, S: Into<MultiSpan>>(&'a self,
313 -> DiagnosticBuilder<'a> {
314 let mut result = DiagnosticBuilder::new(self, Level::Warning, msg);
316 result.code(code.to_owned());
317 if !self.can_emit_warnings {
322 pub fn struct_warn<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> {
323 let mut result = DiagnosticBuilder::new(self, Level::Warning, msg);
324 if !self.can_emit_warnings {
329 pub fn struct_span_err<'a, S: Into<MultiSpan>>(&'a self,
332 -> DiagnosticBuilder<'a> {
333 let mut result = DiagnosticBuilder::new(self, Level::Error, msg);
337 pub fn struct_span_err_with_code<'a, S: Into<MultiSpan>>(&'a self,
341 -> DiagnosticBuilder<'a> {
342 let mut result = DiagnosticBuilder::new(self, Level::Error, msg);
344 result.code(code.to_owned());
347 pub fn struct_err<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> {
348 DiagnosticBuilder::new(self, Level::Error, msg)
350 pub fn struct_span_fatal<'a, S: Into<MultiSpan>>(&'a self,
353 -> DiagnosticBuilder<'a> {
354 let mut result = DiagnosticBuilder::new(self, Level::Fatal, msg);
358 pub fn struct_span_fatal_with_code<'a, S: Into<MultiSpan>>(&'a self,
362 -> DiagnosticBuilder<'a> {
363 let mut result = DiagnosticBuilder::new(self, Level::Fatal, msg);
365 result.code(code.to_owned());
368 pub fn struct_fatal<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> {
369 DiagnosticBuilder::new(self, Level::Fatal, msg)
372 pub fn cancel(&self, err: &mut DiagnosticBuilder) {
376 fn panic_if_treat_err_as_bug(&self) {
377 if self.treat_err_as_bug {
378 panic!("encountered error with `-Z treat_err_as_bug");
382 pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> FatalError {
383 self.emit(&sp.into(), msg, Fatal);
384 self.panic_if_treat_err_as_bug();
387 pub fn span_fatal_with_code<S: Into<MultiSpan>>(&self,
392 self.emit_with_code(&sp.into(), msg, code, Fatal);
393 self.panic_if_treat_err_as_bug();
396 pub fn span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
397 self.emit(&sp.into(), msg, Error);
398 self.panic_if_treat_err_as_bug();
400 pub fn mut_span_err<'a, S: Into<MultiSpan>>(&'a self,
403 -> DiagnosticBuilder<'a> {
404 let mut result = DiagnosticBuilder::new(self, Level::Error, msg);
408 pub fn span_err_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: &str) {
409 self.emit_with_code(&sp.into(), msg, code, Error);
410 self.panic_if_treat_err_as_bug();
412 pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
413 self.emit(&sp.into(), msg, Warning);
415 pub fn span_warn_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: &str) {
416 self.emit_with_code(&sp.into(), msg, code, Warning);
418 pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
419 self.emit(&sp.into(), msg, Bug);
422 pub fn delay_span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
423 if self.treat_err_as_bug {
424 self.span_bug(sp, msg);
426 let mut delayed = self.delayed_span_bug.borrow_mut();
427 *delayed = Some((sp.into(), msg.to_string()));
429 pub fn span_bug_no_panic<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
430 self.emit(&sp.into(), msg, Bug);
432 pub fn span_note_without_error<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
433 self.emit(&sp.into(), msg, Note);
435 pub fn span_note_diag<'a>(&'a self,
438 -> DiagnosticBuilder<'a> {
439 let mut db = DiagnosticBuilder::new(self, Note, msg);
443 pub fn span_unimpl<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
444 self.span_bug(sp, &format!("unimplemented {}", msg));
446 pub fn fatal(&self, msg: &str) -> FatalError {
447 if self.treat_err_as_bug {
450 let mut db = DiagnosticBuilder::new(self, Fatal, msg);
454 pub fn err(&self, msg: &str) {
455 if self.treat_err_as_bug {
458 let mut db = DiagnosticBuilder::new(self, Error, msg);
461 pub fn warn(&self, msg: &str) {
462 let mut db = DiagnosticBuilder::new(self, Warning, msg);
465 pub fn note_without_error(&self, msg: &str) {
466 let mut db = DiagnosticBuilder::new(self, Note, msg);
469 pub fn bug(&self, msg: &str) -> ! {
470 let mut db = DiagnosticBuilder::new(self, Bug, msg);
474 pub fn unimpl(&self, msg: &str) -> ! {
475 self.bug(&format!("unimplemented {}", msg));
478 pub fn bump_err_count(&self) {
479 self.err_count.set(self.err_count.get() + 1);
482 pub fn err_count(&self) -> usize {
486 pub fn has_errors(&self) -> bool {
487 self.err_count.get() > 0
489 pub fn abort_if_errors(&self) {
491 match self.err_count.get() {
493 let delayed_bug = self.delayed_span_bug.borrow();
495 Some((ref span, ref errmsg)) => {
496 self.span_bug(span.clone(), errmsg);
503 1 => s = "aborting due to previous error".to_string(),
505 s = format!("aborting due to {} previous errors", self.err_count.get());
509 panic!(self.fatal(&s));
511 pub fn emit(&self, msp: &MultiSpan, msg: &str, lvl: Level) {
512 if lvl == Warning && !self.can_emit_warnings {
515 let mut db = DiagnosticBuilder::new(self, lvl, msg);
516 db.set_span(msp.clone());
518 if !self.continue_after_error.get() {
519 self.abort_if_errors();
522 pub fn emit_with_code(&self, msp: &MultiSpan, msg: &str, code: &str, lvl: Level) {
523 if lvl == Warning && !self.can_emit_warnings {
526 let mut db = DiagnosticBuilder::new_with_code(self, lvl, Some(code.to_owned()), msg);
527 db.set_span(msp.clone());
529 if !self.continue_after_error.get() {
530 self.abort_if_errors();
536 #[derive(Copy, PartialEq, Clone, Debug, RustcEncodable, RustcDecodable)]
540 // An error which while not immediately fatal, should stop the compiler
541 // progressing beyond the current phase.
550 impl fmt::Display for Level {
551 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
557 pub fn color(self) -> term::color::Color {
559 Bug | Fatal | PhaseFatal | Error => term::color::BRIGHT_RED,
562 term::color::BRIGHT_YELLOW
567 Note => term::color::BRIGHT_GREEN,
568 Help => term::color::BRIGHT_CYAN,
569 Cancelled => unreachable!(),
573 pub fn to_str(self) -> &'static str {
575 Bug => "error: internal compiler error",
576 Fatal | PhaseFatal | Error => "error",
577 Warning => "warning",
580 Cancelled => panic!("Shouldn't call on cancelled error"),
585 pub fn expect<T, M>(diag: &Handler, opt: Option<T>, msg: M) -> T
586 where M: FnOnce() -> String
590 None => diag.bug(&msg()),