]> git.proxmox.com Git - rustc.git/blob - src/librustc_errors/lib.rs
New upstream version 1.23.0+dfsg1
[rustc.git] / src / librustc_errors / lib.rs
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.
4 //
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.
10
11 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
12 html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
13 html_root_url = "https://doc.rust-lang.org/nightly/")]
14 #![deny(warnings)]
15
16 #![feature(custom_attribute)]
17 #![allow(unused_attributes)]
18 #![feature(range_contains)]
19 #![cfg_attr(unix, feature(libc))]
20 #![feature(conservative_impl_trait)]
21 #![feature(i128_type)]
22
23 extern crate term;
24 #[cfg(unix)]
25 extern crate libc;
26 extern crate rustc_data_structures;
27 extern crate serialize as rustc_serialize;
28 extern crate syntax_pos;
29
30 pub use emitter::ColorConfig;
31
32 use self::Level::*;
33
34 use emitter::{Emitter, EmitterWriter};
35
36 use rustc_data_structures::fx::FxHashSet;
37 use rustc_data_structures::stable_hasher::StableHasher;
38
39 use std::borrow::Cow;
40 use std::cell::{RefCell, Cell};
41 use std::mem;
42 use std::rc::Rc;
43 use std::{error, fmt};
44
45 mod diagnostic;
46 mod diagnostic_builder;
47 pub mod emitter;
48 mod snippet;
49 pub mod registry;
50 mod styled_buffer;
51 mod lock;
52
53 use syntax_pos::{BytePos, Loc, FileLinesResult, FileMap, FileName, MultiSpan, Span, NO_EXPANSION};
54
55 #[derive(Clone, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)]
56 pub struct CodeSuggestion {
57 /// Each substitute can have multiple variants due to multiple
58 /// applicable suggestions
59 ///
60 /// `foo.bar` might be replaced with `a.b` or `x.y` by replacing
61 /// `foo` and `bar` on their own:
62 ///
63 /// ```
64 /// vec![
65 /// Substitution { parts: vec![(0..3, "a"), (4..7, "b")] },
66 /// Substitution { parts: vec![(0..3, "x"), (4..7, "y")] },
67 /// ]
68 /// ```
69 ///
70 /// or by replacing the entire span:
71 ///
72 /// ```
73 /// vec![
74 /// Substitution { parts: vec![(0..7, "a.b")] },
75 /// Substitution { parts: vec![(0..7, "x.y")] },
76 /// ]
77 /// ```
78 pub substitutions: Vec<Substitution>,
79 pub msg: String,
80 pub show_code_when_inline: bool,
81 }
82
83 #[derive(Clone, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)]
84 /// See the docs on `CodeSuggestion::substitutions`
85 pub struct Substitution {
86 pub parts: Vec<SubstitutionPart>,
87 }
88
89 #[derive(Clone, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)]
90 pub struct SubstitutionPart {
91 pub span: Span,
92 pub snippet: String,
93 }
94
95 pub trait CodeMapper {
96 fn lookup_char_pos(&self, pos: BytePos) -> Loc;
97 fn span_to_lines(&self, sp: Span) -> FileLinesResult;
98 fn span_to_string(&self, sp: Span) -> String;
99 fn span_to_filename(&self, sp: Span) -> FileName;
100 fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span>;
101 fn call_span_if_macro(&self, sp: Span) -> Span;
102 fn ensure_filemap_source_present(&self, file_map: Rc<FileMap>) -> bool;
103 }
104
105 impl CodeSuggestion {
106 /// Returns the assembled code suggestions and whether they should be shown with an underline.
107 pub fn splice_lines(&self, cm: &CodeMapper) -> Vec<(String, Vec<SubstitutionPart>)> {
108 use syntax_pos::{CharPos, Loc, Pos};
109
110 fn push_trailing(buf: &mut String,
111 line_opt: Option<&Cow<str>>,
112 lo: &Loc,
113 hi_opt: Option<&Loc>) {
114 let (lo, hi_opt) = (lo.col.to_usize(), hi_opt.map(|hi| hi.col.to_usize()));
115 if let Some(line) = line_opt {
116 if let Some(lo) = line.char_indices().map(|(i, _)| i).nth(lo) {
117 let hi_opt = hi_opt.and_then(|hi| line.char_indices().map(|(i, _)| i).nth(hi));
118 buf.push_str(match hi_opt {
119 Some(hi) => &line[lo..hi],
120 None => &line[lo..],
121 });
122 }
123 if let None = hi_opt {
124 buf.push('\n');
125 }
126 }
127 }
128
129 assert!(!self.substitutions.is_empty());
130
131 self.substitutions.iter().cloned().map(|mut substitution| {
132 // Assumption: all spans are in the same file, and all spans
133 // are disjoint. Sort in ascending order.
134 substitution.parts.sort_by_key(|part| part.span.lo());
135
136 // Find the bounding span.
137 let lo = substitution.parts.iter().map(|part| part.span.lo()).min().unwrap();
138 let hi = substitution.parts.iter().map(|part| part.span.hi()).min().unwrap();
139 let bounding_span = Span::new(lo, hi, NO_EXPANSION);
140 let lines = cm.span_to_lines(bounding_span).unwrap();
141 assert!(!lines.lines.is_empty());
142
143 // To build up the result, we do this for each span:
144 // - push the line segment trailing the previous span
145 // (at the beginning a "phantom" span pointing at the start of the line)
146 // - push lines between the previous and current span (if any)
147 // - if the previous and current span are not on the same line
148 // push the line segment leading up to the current span
149 // - splice in the span substitution
150 //
151 // Finally push the trailing line segment of the last span
152 let fm = &lines.file;
153 let mut prev_hi = cm.lookup_char_pos(bounding_span.lo());
154 prev_hi.col = CharPos::from_usize(0);
155
156 let mut prev_line = fm.get_line(lines.lines[0].line_index);
157 let mut buf = String::new();
158
159 for part in &substitution.parts {
160 let cur_lo = cm.lookup_char_pos(part.span.lo());
161 if prev_hi.line == cur_lo.line {
162 push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, Some(&cur_lo));
163 } else {
164 push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, None);
165 // push lines between the previous and current span (if any)
166 for idx in prev_hi.line..(cur_lo.line - 1) {
167 if let Some(line) = fm.get_line(idx) {
168 buf.push_str(line.as_ref());
169 buf.push('\n');
170 }
171 }
172 if let Some(cur_line) = fm.get_line(cur_lo.line - 1) {
173 buf.push_str(&cur_line[..cur_lo.col.to_usize()]);
174 }
175 }
176 buf.push_str(&part.snippet);
177 prev_hi = cm.lookup_char_pos(part.span.hi());
178 prev_line = fm.get_line(prev_hi.line - 1);
179 }
180 // if the replacement already ends with a newline, don't print the next line
181 if !buf.ends_with('\n') {
182 push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, None);
183 }
184 // remove trailing newlines
185 while buf.ends_with('\n') {
186 buf.pop();
187 }
188 (buf, substitution.parts)
189 }).collect()
190 }
191 }
192
193 /// Used as a return value to signify a fatal error occurred. (It is also
194 /// used as the argument to panic at the moment, but that will eventually
195 /// not be true.)
196 #[derive(Copy, Clone, Debug)]
197 #[must_use]
198 pub struct FatalError;
199
200 impl fmt::Display for FatalError {
201 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
202 write!(f, "parser fatal error")
203 }
204 }
205
206 impl error::Error for FatalError {
207 fn description(&self) -> &str {
208 "The parser has encountered a fatal error"
209 }
210 }
211
212 /// Signifies that the compiler died with an explicit call to `.bug`
213 /// or `.span_bug` rather than a failed assertion, etc.
214 #[derive(Copy, Clone, Debug)]
215 pub struct ExplicitBug;
216
217 impl fmt::Display for ExplicitBug {
218 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
219 write!(f, "parser internal bug")
220 }
221 }
222
223 impl error::Error for ExplicitBug {
224 fn description(&self) -> &str {
225 "The parser has encountered an internal bug"
226 }
227 }
228
229 pub use diagnostic::{Diagnostic, SubDiagnostic, DiagnosticStyledString, DiagnosticId};
230 pub use diagnostic_builder::DiagnosticBuilder;
231
232 /// A handler deals with errors; certain errors
233 /// (fatal, bug, unimpl) may cause immediate exit,
234 /// others log errors for later reporting.
235 pub struct Handler {
236 err_count: Cell<usize>,
237 emitter: RefCell<Box<Emitter>>,
238 pub can_emit_warnings: bool,
239 treat_err_as_bug: bool,
240 continue_after_error: Cell<bool>,
241 delayed_span_bug: RefCell<Option<Diagnostic>>,
242 tracked_diagnostics: RefCell<Option<Vec<Diagnostic>>>,
243
244 // This set contains a hash of every diagnostic that has been emitted by
245 // this handler. These hashes is used to avoid emitting the same error
246 // twice.
247 emitted_diagnostics: RefCell<FxHashSet<u128>>,
248 }
249
250 impl Handler {
251 pub fn with_tty_emitter(color_config: ColorConfig,
252 can_emit_warnings: bool,
253 treat_err_as_bug: bool,
254 cm: Option<Rc<CodeMapper>>)
255 -> Handler {
256 let emitter = Box::new(EmitterWriter::stderr(color_config, cm, false));
257 Handler::with_emitter(can_emit_warnings, treat_err_as_bug, emitter)
258 }
259
260 pub fn with_emitter(can_emit_warnings: bool,
261 treat_err_as_bug: bool,
262 e: Box<Emitter>)
263 -> Handler {
264 Handler {
265 err_count: Cell::new(0),
266 emitter: RefCell::new(e),
267 can_emit_warnings,
268 treat_err_as_bug,
269 continue_after_error: Cell::new(true),
270 delayed_span_bug: RefCell::new(None),
271 tracked_diagnostics: RefCell::new(None),
272 emitted_diagnostics: RefCell::new(FxHashSet()),
273 }
274 }
275
276 pub fn set_continue_after_error(&self, continue_after_error: bool) {
277 self.continue_after_error.set(continue_after_error);
278 }
279
280 // NOTE: DO NOT call this function from rustc, as it relies on `err_count` being non-zero
281 // if an error happened to avoid ICEs. This function should only be called from tools.
282 pub fn reset_err_count(&self) {
283 self.err_count.set(0);
284 }
285
286 pub fn struct_dummy<'a>(&'a self) -> DiagnosticBuilder<'a> {
287 DiagnosticBuilder::new(self, Level::Cancelled, "")
288 }
289
290 pub fn struct_span_warn<'a, S: Into<MultiSpan>>(&'a self,
291 sp: S,
292 msg: &str)
293 -> DiagnosticBuilder<'a> {
294 let mut result = DiagnosticBuilder::new(self, Level::Warning, msg);
295 result.set_span(sp);
296 if !self.can_emit_warnings {
297 result.cancel();
298 }
299 result
300 }
301 pub fn struct_span_warn_with_code<'a, S: Into<MultiSpan>>(&'a self,
302 sp: S,
303 msg: &str,
304 code: DiagnosticId)
305 -> DiagnosticBuilder<'a> {
306 let mut result = DiagnosticBuilder::new(self, Level::Warning, msg);
307 result.set_span(sp);
308 result.code(code);
309 if !self.can_emit_warnings {
310 result.cancel();
311 }
312 result
313 }
314 pub fn struct_warn<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> {
315 let mut result = DiagnosticBuilder::new(self, Level::Warning, msg);
316 if !self.can_emit_warnings {
317 result.cancel();
318 }
319 result
320 }
321 pub fn struct_span_err<'a, S: Into<MultiSpan>>(&'a self,
322 sp: S,
323 msg: &str)
324 -> DiagnosticBuilder<'a> {
325 let mut result = DiagnosticBuilder::new(self, Level::Error, msg);
326 result.set_span(sp);
327 result
328 }
329 pub fn struct_span_err_with_code<'a, S: Into<MultiSpan>>(&'a self,
330 sp: S,
331 msg: &str,
332 code: DiagnosticId)
333 -> DiagnosticBuilder<'a> {
334 let mut result = DiagnosticBuilder::new(self, Level::Error, msg);
335 result.set_span(sp);
336 result.code(code);
337 result
338 }
339 // FIXME: This method should be removed (every error should have an associated error code).
340 pub fn struct_err<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> {
341 DiagnosticBuilder::new(self, Level::Error, msg)
342 }
343 pub fn struct_err_with_code<'a>(
344 &'a self,
345 msg: &str,
346 code: DiagnosticId,
347 ) -> DiagnosticBuilder<'a> {
348 let mut result = DiagnosticBuilder::new(self, Level::Error, msg);
349 result.code(code);
350 result
351 }
352 pub fn struct_span_fatal<'a, S: Into<MultiSpan>>(&'a self,
353 sp: S,
354 msg: &str)
355 -> DiagnosticBuilder<'a> {
356 let mut result = DiagnosticBuilder::new(self, Level::Fatal, msg);
357 result.set_span(sp);
358 result
359 }
360 pub fn struct_span_fatal_with_code<'a, S: Into<MultiSpan>>(&'a self,
361 sp: S,
362 msg: &str,
363 code: DiagnosticId)
364 -> DiagnosticBuilder<'a> {
365 let mut result = DiagnosticBuilder::new(self, Level::Fatal, msg);
366 result.set_span(sp);
367 result.code(code);
368 result
369 }
370 pub fn struct_fatal<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> {
371 DiagnosticBuilder::new(self, Level::Fatal, msg)
372 }
373
374 pub fn cancel(&self, err: &mut DiagnosticBuilder) {
375 err.cancel();
376 }
377
378 fn panic_if_treat_err_as_bug(&self) {
379 if self.treat_err_as_bug {
380 panic!("encountered error with `-Z treat_err_as_bug");
381 }
382 }
383
384 pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> FatalError {
385 self.emit(&sp.into(), msg, Fatal);
386 FatalError
387 }
388 pub fn span_fatal_with_code<S: Into<MultiSpan>>(&self,
389 sp: S,
390 msg: &str,
391 code: DiagnosticId)
392 -> FatalError {
393 self.emit_with_code(&sp.into(), msg, code, Fatal);
394 FatalError
395 }
396 pub fn span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
397 self.emit(&sp.into(), msg, Error);
398 }
399 pub fn mut_span_err<'a, S: Into<MultiSpan>>(&'a self,
400 sp: S,
401 msg: &str)
402 -> DiagnosticBuilder<'a> {
403 let mut result = DiagnosticBuilder::new(self, Level::Error, msg);
404 result.set_span(sp);
405 result
406 }
407 pub fn span_err_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: DiagnosticId) {
408 self.emit_with_code(&sp.into(), msg, code, Error);
409 }
410 pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
411 self.emit(&sp.into(), msg, Warning);
412 }
413 pub fn span_warn_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: DiagnosticId) {
414 self.emit_with_code(&sp.into(), msg, code, Warning);
415 }
416 pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
417 self.emit(&sp.into(), msg, Bug);
418 panic!(ExplicitBug);
419 }
420 pub fn delay_span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
421 if self.treat_err_as_bug {
422 self.span_bug(sp, msg);
423 }
424 let mut diagnostic = Diagnostic::new(Level::Bug, msg);
425 diagnostic.set_span(sp.into());
426 *self.delayed_span_bug.borrow_mut() = Some(diagnostic);
427 }
428 pub fn span_bug_no_panic<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
429 self.emit(&sp.into(), msg, Bug);
430 }
431 pub fn span_note_without_error<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
432 self.emit(&sp.into(), msg, Note);
433 }
434 pub fn span_note_diag<'a>(&'a self,
435 sp: Span,
436 msg: &str)
437 -> DiagnosticBuilder<'a> {
438 let mut db = DiagnosticBuilder::new(self, Note, msg);
439 db.set_span(sp);
440 db
441 }
442 pub fn span_unimpl<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
443 self.span_bug(sp, &format!("unimplemented {}", msg));
444 }
445 pub fn fatal(&self, msg: &str) -> FatalError {
446 if self.treat_err_as_bug {
447 self.bug(msg);
448 }
449 let mut db = DiagnosticBuilder::new(self, Fatal, msg);
450 db.emit();
451 FatalError
452 }
453 pub fn err(&self, msg: &str) {
454 if self.treat_err_as_bug {
455 self.bug(msg);
456 }
457 let mut db = DiagnosticBuilder::new(self, Error, msg);
458 db.emit();
459 }
460 pub fn warn(&self, msg: &str) {
461 let mut db = DiagnosticBuilder::new(self, Warning, msg);
462 db.emit();
463 }
464 pub fn note_without_error(&self, msg: &str) {
465 let mut db = DiagnosticBuilder::new(self, Note, msg);
466 db.emit();
467 }
468 pub fn bug(&self, msg: &str) -> ! {
469 let mut db = DiagnosticBuilder::new(self, Bug, msg);
470 db.emit();
471 panic!(ExplicitBug);
472 }
473 pub fn unimpl(&self, msg: &str) -> ! {
474 self.bug(&format!("unimplemented {}", msg));
475 }
476
477 fn bump_err_count(&self) {
478 self.panic_if_treat_err_as_bug();
479 self.err_count.set(self.err_count.get() + 1);
480 }
481
482 pub fn err_count(&self) -> usize {
483 self.err_count.get()
484 }
485
486 pub fn has_errors(&self) -> bool {
487 self.err_count.get() > 0
488 }
489 pub fn abort_if_errors(&self) {
490 let s;
491 match self.err_count.get() {
492 0 => {
493 if let Some(bug) = self.delayed_span_bug.borrow_mut().take() {
494 DiagnosticBuilder::new_diagnostic(self, bug).emit();
495 }
496 return;
497 }
498 1 => s = "aborting due to previous error".to_string(),
499 _ => {
500 s = format!("aborting due to {} previous errors", self.err_count.get());
501 }
502 }
503
504 panic!(self.fatal(&s));
505 }
506 pub fn emit(&self, msp: &MultiSpan, msg: &str, lvl: Level) {
507 if lvl == Warning && !self.can_emit_warnings {
508 return;
509 }
510 let mut db = DiagnosticBuilder::new(self, lvl, msg);
511 db.set_span(msp.clone());
512 db.emit();
513 if !self.continue_after_error.get() {
514 self.abort_if_errors();
515 }
516 }
517 pub fn emit_with_code(&self, msp: &MultiSpan, msg: &str, code: DiagnosticId, lvl: Level) {
518 if lvl == Warning && !self.can_emit_warnings {
519 return;
520 }
521 let mut db = DiagnosticBuilder::new_with_code(self, lvl, Some(code), msg);
522 db.set_span(msp.clone());
523 db.emit();
524 if !self.continue_after_error.get() {
525 self.abort_if_errors();
526 }
527 }
528
529 pub fn track_diagnostics<F, R>(&self, f: F) -> (R, Vec<Diagnostic>)
530 where F: FnOnce() -> R
531 {
532 let prev = mem::replace(&mut *self.tracked_diagnostics.borrow_mut(),
533 Some(Vec::new()));
534 let ret = f();
535 let diagnostics = mem::replace(&mut *self.tracked_diagnostics.borrow_mut(), prev)
536 .unwrap();
537 (ret, diagnostics)
538 }
539
540 fn emit_db(&self, db: &DiagnosticBuilder) {
541 let diagnostic = &**db;
542
543 if let Some(ref mut list) = *self.tracked_diagnostics.borrow_mut() {
544 list.push(diagnostic.clone());
545 }
546
547 let diagnostic_hash = {
548 use std::hash::Hash;
549 let mut hasher = StableHasher::new();
550 diagnostic.hash(&mut hasher);
551 hasher.finish()
552 };
553
554 // Only emit the diagnostic if we haven't already emitted an equivalent
555 // one:
556 if self.emitted_diagnostics.borrow_mut().insert(diagnostic_hash) {
557 self.emitter.borrow_mut().emit(db);
558 }
559 }
560 }
561
562
563 #[derive(Copy, PartialEq, Clone, Hash, Debug, RustcEncodable, RustcDecodable)]
564 pub enum Level {
565 Bug,
566 Fatal,
567 // An error which while not immediately fatal, should stop the compiler
568 // progressing beyond the current phase.
569 PhaseFatal,
570 Error,
571 Warning,
572 Note,
573 Help,
574 Cancelled,
575 }
576
577 impl fmt::Display for Level {
578 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
579 self.to_str().fmt(f)
580 }
581 }
582
583 impl Level {
584 fn color(self) -> term::color::Color {
585 match self {
586 Bug | Fatal | PhaseFatal | Error => term::color::BRIGHT_RED,
587 Warning => {
588 if cfg!(windows) {
589 term::color::BRIGHT_YELLOW
590 } else {
591 term::color::YELLOW
592 }
593 }
594 Note => term::color::BRIGHT_GREEN,
595 Help => term::color::BRIGHT_CYAN,
596 Cancelled => unreachable!(),
597 }
598 }
599
600 pub fn to_str(self) -> &'static str {
601 match self {
602 Bug => "error: internal compiler error",
603 Fatal | PhaseFatal | Error => "error",
604 Warning => "warning",
605 Note => "note",
606 Help => "help",
607 Cancelled => panic!("Shouldn't call on cancelled error"),
608 }
609 }
610 }