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