swc_common/errors/
mod.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
11use std::{
12    borrow::Cow,
13    cell::RefCell,
14    error, fmt,
15    io::Write,
16    panic,
17    sync::atomic::{AtomicUsize, Ordering::SeqCst},
18};
19
20use rustc_hash::FxHashSet;
21#[cfg(feature = "tty-emitter")]
22use termcolor::{Color, ColorSpec};
23
24use self::Level::*;
25pub use self::{
26    diagnostic::{Diagnostic, DiagnosticId, DiagnosticStyledString, Message, SubDiagnostic},
27    diagnostic_builder::DiagnosticBuilder,
28    emitter::{ColorConfig, Emitter, EmitterWriter},
29    snippet::Style,
30};
31use crate::{
32    rustc_data_structures::stable_hasher::StableHasher,
33    sync::{Lock, LockCell, Lrc},
34    syntax_pos::{BytePos, FileLinesResult, FileName, Loc, MultiSpan, Span},
35    SpanSnippetError,
36};
37
38mod diagnostic;
39mod diagnostic_builder;
40
41pub mod emitter;
42mod lock;
43mod snippet;
44mod styled_buffer;
45
46#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
47#[cfg_attr(
48    feature = "diagnostic-serde",
49    derive(serde::Serialize, serde::Deserialize)
50)]
51#[cfg_attr(
52    any(feature = "rkyv-impl"),
53    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
54)]
55#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
56#[cfg_attr(feature = "rkyv-impl", repr(u32))]
57pub enum Applicability {
58    MachineApplicable,
59    HasPlaceholders,
60    MaybeIncorrect,
61    Unspecified,
62}
63
64#[derive(Clone, Debug, PartialEq, Eq, Hash)]
65#[cfg_attr(
66    feature = "diagnostic-serde",
67    derive(serde::Serialize, serde::Deserialize)
68)]
69#[cfg_attr(
70    any(feature = "rkyv-impl"),
71    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
72)]
73#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
74#[cfg_attr(feature = "rkyv-impl", repr(C))]
75pub struct CodeSuggestion {
76    /// Each substitute can have multiple variants due to multiple
77    /// applicable suggestions
78    ///
79    /// `foo.bar` might be replaced with `a.b` or `x.y` by replacing
80    /// `foo` and `bar` on their own:
81    ///
82    /// ```rust,ignore
83    /// vec![
84    ///     Substitution {
85    ///         parts: vec![(0..3, "a"), (4..7, "b")],
86    ///     },
87    ///     Substitution {
88    ///         parts: vec![(0..3, "x"), (4..7, "y")],
89    ///     },
90    /// ]
91    /// ```
92    ///
93    /// or by replacing the entire span:
94    ///
95    /// ```rust,ignore
96    /// vec![
97    ///     Substitution {
98    ///         parts: vec![(0..7, "a.b")],
99    ///     },
100    ///     Substitution {
101    ///         parts: vec![(0..7, "x.y")],
102    ///     },
103    /// ]
104    /// ```
105    pub substitutions: Vec<Substitution>,
106    pub msg: String,
107    pub show_code_when_inline: bool,
108    /// Whether or not the suggestion is approximate
109    ///
110    /// Sometimes we may show suggestions with placeholders,
111    /// which are useful for users but not useful for
112    /// tools like rustfix
113    pub applicability: Applicability,
114}
115
116#[derive(Clone, Debug, PartialEq, Eq, Hash)]
117/// See the docs on `CodeSuggestion::substitutions`
118#[cfg_attr(
119    feature = "diagnostic-serde",
120    derive(serde::Serialize, serde::Deserialize)
121)]
122#[cfg_attr(
123    any(feature = "rkyv-impl"),
124    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
125)]
126#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
127#[cfg_attr(feature = "rkyv-impl", repr(C))]
128pub struct Substitution {
129    pub parts: Vec<SubstitutionPart>,
130}
131
132#[derive(Clone, Debug, PartialEq, Eq, Hash)]
133#[cfg_attr(
134    feature = "diagnostic-serde",
135    derive(serde::Serialize, serde::Deserialize)
136)]
137#[cfg_attr(
138    any(feature = "rkyv-impl"),
139    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
140)]
141#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
142#[cfg_attr(feature = "rkyv-impl", repr(C))]
143pub struct SubstitutionPart {
144    pub span: Span,
145    pub snippet: String,
146}
147
148pub type SourceMapperDyn = dyn SourceMapper;
149
150pub trait SourceMapper: crate::sync::Send + crate::sync::Sync {
151    fn lookup_char_pos(&self, pos: BytePos) -> Loc;
152    fn span_to_lines(&self, sp: Span) -> FileLinesResult;
153    fn span_to_string(&self, sp: Span) -> String;
154    fn span_to_filename(&self, sp: Span) -> Lrc<FileName>;
155    fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span>;
156    fn call_span_if_macro(&self, sp: Span) -> Span;
157    fn doctest_offset_line(&self, line: usize) -> usize;
158    fn span_to_snippet(&self, sp: Span) -> Result<String, Box<SpanSnippetError>>;
159}
160
161impl CodeSuggestion {
162    /// Returns the assembled code suggestions and whether they should be shown
163    /// with an underline.
164    pub fn splice_lines(&self, cm: &SourceMapperDyn) -> Vec<(String, Vec<SubstitutionPart>)> {
165        use crate::syntax_pos::{CharPos, SmallPos};
166
167        fn push_trailing(
168            buf: &mut String,
169            line_opt: Option<&Cow<'_, str>>,
170            lo: &Loc,
171            hi_opt: Option<&Loc>,
172        ) {
173            let (lo, hi_opt) = (lo.col.to_usize(), hi_opt.map(|hi| hi.col.to_usize()));
174            if let Some(line) = line_opt {
175                if let Some(lo) = line.char_indices().map(|(i, _)| i).nth(lo) {
176                    let hi_opt = hi_opt.and_then(|hi| line.char_indices().map(|(i, _)| i).nth(hi));
177                    buf.push_str(match hi_opt {
178                        Some(hi) => &line[lo..hi],
179                        None => &line[lo..],
180                    });
181                }
182                if hi_opt.is_none() {
183                    buf.push('\n');
184                }
185            }
186        }
187
188        assert!(!self.substitutions.is_empty());
189
190        self.substitutions
191            .iter()
192            .cloned()
193            .map(|mut substitution| {
194                // Assumption: all spans are in the same file, and all spans
195                // are disjoint. Sort in ascending order.
196                substitution.parts.sort_by_key(|part| part.span.lo());
197
198                // Find the bounding span.
199                let lo = substitution
200                    .parts
201                    .iter()
202                    .map(|part| part.span.lo())
203                    .min()
204                    .unwrap();
205                let hi = substitution
206                    .parts
207                    .iter()
208                    .map(|part| part.span.hi())
209                    .min()
210                    .unwrap();
211                let bounding_span = Span::new(lo, hi);
212                let lines = cm.span_to_lines(bounding_span).unwrap();
213                assert!(!lines.lines.is_empty());
214
215                // To build up the result, we do this for each span:
216                // - push the line segment trailing the previous span (at the beginning a
217                //   "phantom" span pointing at the start of the line)
218                // - push lines between the previous and current span (if any)
219                // - if the previous and current span are not on the same line push the line
220                //   segment leading up to the current span
221                // - splice in the span substitution
222                //
223                // Finally push the trailing line segment of the last span
224                let fm = &lines.file;
225                let mut prev_hi = cm.lookup_char_pos(bounding_span.lo());
226                prev_hi.col = CharPos::from_usize(0);
227
228                let mut prev_line = fm.get_line(lines.lines[0].line_index);
229                let mut buf = String::new();
230
231                for part in &substitution.parts {
232                    let cur_lo = cm.lookup_char_pos(part.span.lo());
233                    if prev_hi.line == cur_lo.line {
234                        push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, Some(&cur_lo));
235                    } else {
236                        push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, None);
237                        // push lines between the previous and current span (if any)
238                        for idx in prev_hi.line..(cur_lo.line - 1) {
239                            if let Some(line) = fm.get_line(idx) {
240                                buf.push_str(line.as_ref());
241                                buf.push('\n');
242                            }
243                        }
244                        if let Some(cur_line) = fm.get_line(cur_lo.line - 1) {
245                            buf.push_str(&cur_line[..cur_lo.col.to_usize()]);
246                        }
247                    }
248                    buf.push_str(&part.snippet);
249                    prev_hi = cm.lookup_char_pos(part.span.hi());
250                    prev_line = fm.get_line(prev_hi.line - 1);
251                }
252                // if the replacement already ends with a newline, don't print the next line
253                if !buf.ends_with('\n') {
254                    push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, None);
255                }
256                // remove trailing newlines
257                while buf.ends_with('\n') {
258                    buf.pop();
259                }
260                (buf, substitution.parts)
261            })
262            .collect()
263    }
264}
265
266/// Used as a return value to signify a fatal error occurred. (It is also
267/// used as the argument to panic at the moment, but that will eventually
268/// not be true.)
269#[derive(Copy, Clone, Debug)]
270#[must_use]
271pub struct FatalError;
272
273pub struct FatalErrorMarker;
274
275// // Don't implement Send on FatalError. This makes it impossible to
276// // panic!(FatalError). We don't want to invoke the panic handler and print a
277// // backtrace for fatal errors.
278// impl !Send for FatalError {}
279
280impl FatalError {
281    pub fn raise(self) -> ! {
282        panic::resume_unwind(Box::new(FatalErrorMarker))
283    }
284}
285
286impl fmt::Display for FatalError {
287    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
288        write!(f, "parser fatal error")
289    }
290}
291
292impl error::Error for FatalError {
293    fn description(&self) -> &str {
294        "The parser has encountered a fatal error"
295    }
296}
297
298/// Signifies that the compiler died with an explicit call to `.bug`
299/// or `.span_bug` rather than a failed assertion, etc.
300#[derive(Copy, Clone, Debug)]
301pub struct ExplicitBug;
302
303impl fmt::Display for ExplicitBug {
304    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
305        write!(f, "parser internal bug")
306    }
307}
308
309impl error::Error for ExplicitBug {
310    fn description(&self) -> &str {
311        "The parser has encountered an internal bug"
312    }
313}
314
315/// A handler deals with errors; certain errors
316/// (fatal, bug, unimpl) may cause immediate exit,
317/// others log errors for later reporting.
318///
319/// # Example
320///
321/// `swc` provides a global-like variable ([HANDLER]) of type `Handler` that can
322/// be used to report errors.
323///
324/// You can refer to [the lint rules](https://github.com/swc-project/swc/tree/main/crates/swc_ecma_lints/src/rules) for other example usages.
325/// All lint rules have code for error reporting.
326///
327/// ## Error reporting in swc
328///
329/// ```rust,ignore
330/// use swc_common::errors::HANDLER;
331///
332/// # fn main() {
333///     HANDLER.with(|handler| {
334///         // You can access the handler for the current file using HANDLER.with.
335///
336///         // We now report an error
337///
338///         // `struct_span_err` creates a builder for a diagnostic.
339///         // The span passed to `struct_span_err` will used to point the problematic code.
340///         //
341///         // You may provide additional information, like a previous declaration of parameter.
342///         handler
343///             .struct_span_err(
344///                 span,
345///                 &format!("`{}` used as parameter more than once", atom),
346///             )
347///             .span_note(
348///                 old_span,
349///                 &format!("previous definition of `{}` here", atom),
350///             )
351///             .emit();
352///     });
353/// # }
354/// ```
355pub struct Handler {
356    pub flags: HandlerFlags,
357
358    err_count: AtomicUsize,
359    emitter: Lock<Box<dyn Emitter>>,
360    continue_after_error: LockCell<bool>,
361    delayed_span_bugs: Lock<Vec<Diagnostic>>,
362
363    // This set contains the `DiagnosticId` of all emitted diagnostics to avoid
364    // emitting the same diagnostic with extended help (`--teach`) twice, which
365    // would be unnecessary repetition.
366    taught_diagnostics: Lock<FxHashSet<DiagnosticId>>,
367
368    /// Used to suggest rustc --explain <error code>
369    emitted_diagnostic_codes: Lock<FxHashSet<DiagnosticId>>,
370
371    // This set contains a hash of every diagnostic that has been emitted by
372    // this handler. These hashes is used to avoid emitting the same error
373    // twice.
374    emitted_diagnostics: Lock<FxHashSet<u128>>,
375}
376
377fn default_track_diagnostic(_: &Diagnostic) {}
378
379thread_local!(pub static TRACK_DIAGNOSTICS: RefCell<Box<dyn Fn(&Diagnostic)>> =
380                RefCell::new(Box::new(default_track_diagnostic)));
381
382#[derive(Default)]
383pub struct HandlerFlags {
384    /// If false, warning-level lints are suppressed.
385    /// (rustc: see `--allow warnings` and `--cap-lints`)
386    pub can_emit_warnings: bool,
387    /// If true, error-level diagnostics are upgraded to bug-level.
388    /// (rustc: see `-Z treat-err-as-bug`)
389    pub treat_err_as_bug: bool,
390    /// If true, immediately emit diagnostics that would otherwise be buffered.
391    /// (rustc: see `-Z dont-buffer-diagnostics` and `-Z treat-err-as-bug`)
392    pub dont_buffer_diagnostics: bool,
393    /// If true, immediately print bugs registered with `delay_span_bug`.
394    /// (rustc: see `-Z report-delayed-bugs`)
395    pub report_delayed_bugs: bool,
396    /// show macro backtraces even for non-local macros.
397    /// (rustc: see `-Z external-macro-backtrace`)
398    pub external_macro_backtrace: bool,
399}
400
401impl Drop for Handler {
402    fn drop(&mut self) {
403        if self.err_count() == 0 {
404            let mut bugs = self.delayed_span_bugs.borrow_mut();
405            let has_bugs = !bugs.is_empty();
406            for bug in bugs.drain(..) {
407                DiagnosticBuilder::new_diagnostic(self, bug).emit();
408            }
409            if has_bugs {
410                panic!("no errors encountered even though `delay_span_bug` issued");
411            }
412        }
413    }
414}
415
416impl Handler {
417    #[cfg(feature = "tty-emitter")]
418    #[cfg_attr(docsrs, doc(cfg(feature = "tty-emitter")))]
419    pub fn with_tty_emitter(
420        color_config: ColorConfig,
421        can_emit_warnings: bool,
422        treat_err_as_bug: bool,
423        cm: Option<Lrc<SourceMapperDyn>>,
424    ) -> Handler {
425        Handler::with_tty_emitter_and_flags(
426            color_config,
427            cm,
428            HandlerFlags {
429                can_emit_warnings,
430                treat_err_as_bug,
431                ..Default::default()
432            },
433        )
434    }
435
436    #[cfg(feature = "tty-emitter")]
437    #[cfg_attr(docsrs, doc(cfg(feature = "tty-emitter")))]
438    pub fn with_tty_emitter_and_flags(
439        color_config: ColorConfig,
440        cm: Option<Lrc<SourceMapperDyn>>,
441        flags: HandlerFlags,
442    ) -> Handler {
443        let emitter = Box::new(EmitterWriter::stderr(color_config, cm, false, false));
444        Handler::with_emitter_and_flags(emitter, flags)
445    }
446
447    /// Example implementation of [Emitter] is [EmitterWriter]
448    pub fn with_emitter(
449        can_emit_warnings: bool,
450        treat_err_as_bug: bool,
451        emitter: Box<dyn Emitter>,
452    ) -> Handler {
453        Handler::with_emitter_and_flags(
454            emitter,
455            HandlerFlags {
456                can_emit_warnings,
457                treat_err_as_bug,
458                ..Default::default()
459            },
460        )
461    }
462
463    /// Calls [Self::with_emitter] with [EmitterWriter].
464    pub fn with_emitter_writer(
465        dst: Box<dyn Write + Send>,
466        cm: Option<Lrc<SourceMapperDyn>>,
467    ) -> Handler {
468        Handler::with_emitter(
469            true,
470            false,
471            Box::new(EmitterWriter::new(dst, cm, false, true)),
472        )
473    }
474
475    pub fn with_emitter_and_flags(e: Box<dyn Emitter>, flags: HandlerFlags) -> Handler {
476        Handler {
477            flags,
478            err_count: AtomicUsize::new(0),
479            emitter: Lock::new(e),
480            continue_after_error: LockCell::new(true),
481            delayed_span_bugs: Lock::new(Vec::new()),
482            taught_diagnostics: Default::default(),
483            emitted_diagnostic_codes: Default::default(),
484            emitted_diagnostics: Default::default(),
485        }
486    }
487
488    pub fn set_continue_after_error(&self, continue_after_error: bool) {
489        self.continue_after_error.set(continue_after_error);
490    }
491
492    /// Resets the diagnostic error count as well as the cached emitted
493    /// diagnostics.
494    ///
495    /// NOTE: DO NOT call this function from rustc. It is only meant to be
496    /// called from external tools that want to reuse a `Parser` cleaning
497    /// the previously emitted diagnostics as well as the overall count of
498    /// emitted error diagnostics.
499    pub fn reset_err_count(&self) {
500        // actually frees the underlying memory (which `clear` would not do)
501        *self.emitted_diagnostics.borrow_mut() = Default::default();
502        self.err_count.store(0, SeqCst);
503    }
504
505    pub fn struct_dummy(&self) -> DiagnosticBuilder<'_> {
506        DiagnosticBuilder::new(self, Level::Cancelled, "")
507    }
508
509    pub fn struct_span_warn<'a, S: Into<MultiSpan>>(
510        &'a self,
511        sp: S,
512        msg: &str,
513    ) -> DiagnosticBuilder<'a> {
514        let mut result = DiagnosticBuilder::new(self, Level::Warning, msg);
515        result.set_span(sp);
516        if !self.flags.can_emit_warnings {
517            result.cancel();
518        }
519        result
520    }
521
522    pub fn struct_span_warn_with_code<'a, S: Into<MultiSpan>>(
523        &'a self,
524        sp: S,
525        msg: &str,
526        code: DiagnosticId,
527    ) -> DiagnosticBuilder<'a> {
528        let mut result = DiagnosticBuilder::new(self, Level::Warning, msg);
529        result.set_span(sp);
530        result.code(code);
531        if !self.flags.can_emit_warnings {
532            result.cancel();
533        }
534        result
535    }
536
537    pub fn struct_warn<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> {
538        let mut result = DiagnosticBuilder::new(self, Level::Warning, msg);
539        if !self.flags.can_emit_warnings {
540            result.cancel();
541        }
542        result
543    }
544
545    pub fn struct_span_err<'a, S: Into<MultiSpan>>(
546        &'a self,
547        sp: S,
548        msg: &str,
549    ) -> DiagnosticBuilder<'a> {
550        let mut result = DiagnosticBuilder::new(self, Level::Error, msg);
551        result.set_span(sp);
552        result
553    }
554
555    pub fn struct_span_err_with_code<'a, S: Into<MultiSpan>>(
556        &'a self,
557        sp: S,
558        msg: &str,
559        code: DiagnosticId,
560    ) -> DiagnosticBuilder<'a> {
561        let mut result = DiagnosticBuilder::new(self, Level::Error, msg);
562        result.set_span(sp);
563        result.code(code);
564        result
565    }
566
567    // FIXME: This method should be removed (every error should have an associated
568    // error code).
569    pub fn struct_err<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> {
570        DiagnosticBuilder::new(self, Level::Error, msg)
571    }
572
573    pub fn struct_err_with_code<'a>(
574        &'a self,
575        msg: &str,
576        code: DiagnosticId,
577    ) -> DiagnosticBuilder<'a> {
578        let mut result = DiagnosticBuilder::new(self, Level::Error, msg);
579        result.code(code);
580        result
581    }
582
583    pub fn struct_span_fatal<'a, S: Into<MultiSpan>>(
584        &'a self,
585        sp: S,
586        msg: &str,
587    ) -> DiagnosticBuilder<'a> {
588        let mut result = DiagnosticBuilder::new(self, Level::Fatal, msg);
589        result.set_span(sp);
590        result
591    }
592
593    pub fn struct_span_fatal_with_code<'a, S: Into<MultiSpan>>(
594        &'a self,
595        sp: S,
596        msg: &str,
597        code: DiagnosticId,
598    ) -> DiagnosticBuilder<'a> {
599        let mut result = DiagnosticBuilder::new(self, Level::Fatal, msg);
600        result.set_span(sp);
601        result.code(code);
602        result
603    }
604
605    pub fn struct_fatal<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> {
606        DiagnosticBuilder::new(self, Level::Fatal, msg)
607    }
608
609    pub fn cancel(&self, err: &mut DiagnosticBuilder<'_>) {
610        err.cancel();
611    }
612
613    fn panic_if_treat_err_as_bug(&self) {
614        if self.flags.treat_err_as_bug {
615            panic!("encountered error with `-Z treat_err_as_bug");
616        }
617    }
618
619    pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> FatalError {
620        self.emit(&sp.into(), msg, Fatal);
621        FatalError
622    }
623
624    pub fn span_fatal_with_code<S: Into<MultiSpan>>(
625        &self,
626        sp: S,
627        msg: &str,
628        code: DiagnosticId,
629    ) -> FatalError {
630        self.emit_with_code(&sp.into(), msg, code, Fatal);
631        FatalError
632    }
633
634    pub fn span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
635        self.emit(&sp.into(), msg, Error);
636    }
637
638    pub fn mut_span_err<'a, S: Into<MultiSpan>>(
639        &'a self,
640        sp: S,
641        msg: &str,
642    ) -> DiagnosticBuilder<'a> {
643        let mut result = DiagnosticBuilder::new(self, Level::Error, msg);
644        result.set_span(sp);
645        result
646    }
647
648    pub fn span_err_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: DiagnosticId) {
649        self.emit_with_code(&sp.into(), msg, code, Error);
650    }
651
652    pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
653        self.emit(&sp.into(), msg, Warning);
654    }
655
656    pub fn span_warn_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: DiagnosticId) {
657        self.emit_with_code(&sp.into(), msg, code, Warning);
658    }
659
660    pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
661        self.emit(&sp.into(), msg, Bug);
662        panic!("{}", ExplicitBug);
663    }
664
665    pub fn delay_span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
666        if self.flags.treat_err_as_bug {
667            // FIXME: don't abort here if report_delayed_bugs is off
668            self.span_bug(sp, msg);
669        }
670        let mut diagnostic = Diagnostic::new(Level::Bug, msg);
671        diagnostic.set_span(sp.into());
672        self.delay_as_bug(diagnostic);
673    }
674
675    fn delay_as_bug(&self, diagnostic: Diagnostic) {
676        if self.flags.report_delayed_bugs {
677            DiagnosticBuilder::new_diagnostic(self, diagnostic.clone()).emit();
678        }
679        self.delayed_span_bugs.borrow_mut().push(diagnostic);
680    }
681
682    pub fn span_bug_no_panic<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
683        self.emit(&sp.into(), msg, Bug);
684    }
685
686    pub fn span_note_without_error<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
687        self.emit(&sp.into(), msg, Note);
688    }
689
690    pub fn span_note_diag<'a>(&'a self, sp: Span, msg: &str) -> DiagnosticBuilder<'a> {
691        let mut db = DiagnosticBuilder::new(self, Note, msg);
692        db.set_span(sp);
693        db
694    }
695
696    pub fn span_unimpl<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
697        self.span_bug(sp, &format!("unimplemented {msg}"));
698    }
699
700    pub fn failure(&self, msg: &str) {
701        DiagnosticBuilder::new(self, FailureNote, msg).emit()
702    }
703
704    pub fn fatal(&self, msg: &str) -> FatalError {
705        if self.flags.treat_err_as_bug {
706            self.bug(msg);
707        }
708        DiagnosticBuilder::new(self, Fatal, msg).emit();
709        FatalError
710    }
711
712    pub fn err(&self, msg: &str) {
713        if self.flags.treat_err_as_bug {
714            self.bug(msg);
715        }
716        let mut db = DiagnosticBuilder::new(self, Error, msg);
717        db.emit();
718    }
719
720    pub fn err_with_code(&self, msg: &str, code: DiagnosticId) {
721        if self.flags.treat_err_as_bug {
722            self.bug(msg);
723        }
724        let mut db = DiagnosticBuilder::new_with_code(self, Error, Some(code), msg);
725        db.emit();
726    }
727
728    pub fn warn(&self, msg: &str) {
729        let mut db = DiagnosticBuilder::new(self, Warning, msg);
730        db.emit();
731    }
732
733    pub fn note_without_error(&self, msg: &str) {
734        let mut db = DiagnosticBuilder::new(self, Note, msg);
735        db.emit();
736    }
737
738    pub fn bug(&self, msg: &str) -> ! {
739        let mut db = DiagnosticBuilder::new(self, Bug, msg);
740        db.emit();
741        panic!("{}", ExplicitBug);
742    }
743
744    pub fn unimpl(&self, msg: &str) -> ! {
745        self.bug(&format!("unimplemented {msg}"));
746    }
747
748    fn bump_err_count(&self) {
749        self.panic_if_treat_err_as_bug();
750        self.err_count.fetch_add(1, SeqCst);
751    }
752
753    pub fn err_count(&self) -> usize {
754        self.err_count.load(SeqCst)
755    }
756
757    pub fn has_errors(&self) -> bool {
758        self.err_count() > 0
759    }
760
761    pub fn print_error_count(&self) {
762        let s = match self.err_count() {
763            0 => return,
764            1 => "aborting due to previous error".to_string(),
765            _ => format!("aborting due to {} previous errors", self.err_count()),
766        };
767
768        let _ = self.fatal(&s);
769
770        let can_show_explain = self.emitter.borrow().should_show_explain();
771        let are_there_diagnostics = !self.emitted_diagnostic_codes.borrow().is_empty();
772        if can_show_explain && are_there_diagnostics {
773            let mut error_codes = self
774                .emitted_diagnostic_codes
775                .borrow()
776                .iter()
777                .filter_map(|x| match *x {
778                    DiagnosticId::Error(ref s) => Some(s.clone()),
779                    _ => None,
780                })
781                .collect::<Vec<_>>();
782            if !error_codes.is_empty() {
783                error_codes.sort();
784                if error_codes.len() > 1 {
785                    let limit = if error_codes.len() > 9 {
786                        9
787                    } else {
788                        error_codes.len()
789                    };
790                    self.failure(&format!(
791                        "Some errors occurred: {}{}",
792                        error_codes[..limit].join(", "),
793                        if error_codes.len() > 9 { "..." } else { "." }
794                    ));
795                    self.failure(&format!(
796                        "For more information about an error, try `rustc --explain {}`.",
797                        &error_codes[0]
798                    ));
799                } else {
800                    self.failure(&format!(
801                        "For more information about this error, try `rustc --explain {}`.",
802                        &error_codes[0]
803                    ));
804                }
805            }
806        }
807    }
808
809    pub fn abort_if_errors(&self) {
810        if self.err_count() == 0 {
811            return;
812        }
813        FatalError.raise();
814    }
815
816    pub fn emit(&self, msp: &MultiSpan, msg: &str, lvl: Level) {
817        if lvl == Warning && !self.flags.can_emit_warnings {
818            return;
819        }
820        let mut db = DiagnosticBuilder::new(self, lvl, msg);
821        db.set_span(msp.clone());
822        db.emit();
823        if !self.continue_after_error.get() {
824            self.abort_if_errors();
825        }
826    }
827
828    pub fn emit_with_code(&self, msp: &MultiSpan, msg: &str, code: DiagnosticId, lvl: Level) {
829        if lvl == Warning && !self.flags.can_emit_warnings {
830            return;
831        }
832        let mut db = DiagnosticBuilder::new_with_code(self, lvl, Some(code), msg);
833        db.set_span(msp.clone());
834        db.emit();
835        if !self.continue_after_error.get() {
836            self.abort_if_errors();
837        }
838    }
839
840    /// `true` if we haven't taught a diagnostic with this code already.
841    /// The caller must then teach the user about such a diagnostic.
842    ///
843    /// Used to suppress emitting the same error multiple times with extended
844    /// explanation when calling `-Z teach`.
845    pub fn must_teach(&self, code: &DiagnosticId) -> bool {
846        self.taught_diagnostics.borrow_mut().insert(code.clone())
847    }
848
849    pub fn force_print_db(&self, mut db: DiagnosticBuilder<'_>) {
850        self.emitter.borrow_mut().emit(&mut db);
851        db.cancel();
852    }
853
854    fn emit_db(&self, db: &mut DiagnosticBuilder<'_>) {
855        let diagnostic = &**db;
856
857        TRACK_DIAGNOSTICS.with(|track_diagnostics| {
858            track_diagnostics.borrow()(diagnostic);
859        });
860
861        if let Some(ref code) = diagnostic.code {
862            self.emitted_diagnostic_codes
863                .borrow_mut()
864                .insert(code.clone());
865        }
866
867        let diagnostic_hash = {
868            use std::hash::Hash;
869            let mut hasher = StableHasher::new();
870            diagnostic.hash(&mut hasher);
871            hasher.finish()
872        };
873
874        // Only emit the diagnostic if we haven't already emitted an equivalent
875        // one:
876        if self
877            .emitted_diagnostics
878            .borrow_mut()
879            .insert(diagnostic_hash)
880        {
881            self.emitter.borrow_mut().emit(db);
882            if db.is_error() {
883                self.bump_err_count();
884            }
885        }
886    }
887
888    pub fn take_diagnostics(&self) -> Vec<String> {
889        self.emitter.borrow_mut().take_diagnostics()
890    }
891}
892
893#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug, Default)]
894#[cfg_attr(
895    feature = "diagnostic-serde",
896    derive(serde::Serialize, serde::Deserialize)
897)]
898#[cfg_attr(
899    any(feature = "rkyv-impl"),
900    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
901)]
902#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
903#[cfg_attr(feature = "rkyv-impl", repr(u32))]
904pub enum Level {
905    Bug,
906    Fatal,
907    // An error which while not immediately fatal, should stop the compiler
908    // progressing beyond the current phase.
909    PhaseFatal,
910    Error,
911    Warning,
912    Note,
913    Help,
914    #[default]
915    Cancelled,
916    FailureNote,
917}
918
919impl fmt::Display for Level {
920    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
921        self.to_str().fmt(f)
922    }
923}
924
925impl Level {
926    #[cfg(feature = "tty-emitter")]
927    fn color(self) -> ColorSpec {
928        let mut spec = ColorSpec::new();
929        match self {
930            Bug | Fatal | PhaseFatal | Error => {
931                spec.set_fg(Some(Color::Red)).set_intense(true);
932            }
933            Warning => {
934                spec.set_fg(Some(Color::Yellow)).set_intense(cfg!(windows));
935            }
936            Note => {
937                spec.set_fg(Some(Color::Green)).set_intense(true);
938            }
939            Help => {
940                spec.set_fg(Some(Color::Cyan)).set_intense(true);
941            }
942            FailureNote => {}
943            Cancelled => unreachable!(),
944        }
945        spec
946    }
947
948    pub fn to_str(self) -> &'static str {
949        match self {
950            Bug => "error: internal compiler error",
951            Fatal | PhaseFatal | Error => "error",
952            Warning => "warning",
953            Note => "note",
954            Help => "help",
955            FailureNote => "",
956            Cancelled => panic!("Shouldn't call on cancelled error"),
957        }
958    }
959
960    pub fn is_failure_note(self) -> bool {
961        matches!(self, FailureNote)
962    }
963}
964
965better_scoped_tls::scoped_tls!(
966    /// Used for error reporting in transform.
967    ///
968    /// This should be only used for errors from the api which does not returning errors.
969    ///
970    /// e.g.
971    ///  - `parser` should not use this.
972    ///  - `transforms` should use this to report error, as it does not return [Result].
973    ///
974    /// See [Handler] for actual usage examples.
975    pub static HANDLER: Handler
976);