rustc_errors/
lib.rs

1//! Diagnostics creation and emission for `rustc`.
2//!
3//! This module contains the code for creating and emitting diagnostics.
4
5// tidy-alphabetical-start
6#![allow(incomplete_features)]
7#![allow(internal_features)]
8#![allow(rustc::diagnostic_outside_of_impl)]
9#![allow(rustc::untranslatable_diagnostic)]
10#![cfg_attr(not(bootstrap), allow(rustc::direct_use_of_rustc_type_ir))]
11#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
12#![doc(rust_logo)]
13#![feature(array_windows)]
14#![feature(assert_matches)]
15#![feature(associated_type_defaults)]
16#![feature(box_patterns)]
17#![feature(default_field_values)]
18#![feature(error_reporter)]
19#![feature(negative_impls)]
20#![feature(never_type)]
21#![feature(rustc_attrs)]
22#![feature(rustdoc_internals)]
23#![feature(try_blocks)]
24#![feature(yeet_expr)]
25// tidy-alphabetical-end
26
27extern crate self as rustc_errors;
28
29use std::assert_matches::assert_matches;
30use std::backtrace::{Backtrace, BacktraceStatus};
31use std::borrow::Cow;
32use std::cell::Cell;
33use std::error::Report;
34use std::ffi::OsStr;
35use std::hash::Hash;
36use std::io::Write;
37use std::num::NonZero;
38use std::ops::DerefMut;
39use std::path::{Path, PathBuf};
40use std::{fmt, panic};
41
42use Level::*;
43pub use codes::*;
44pub use diagnostic::{
45    BugAbort, Diag, DiagArg, DiagArgMap, DiagArgName, DiagArgValue, DiagInner, DiagStyledString,
46    Diagnostic, EmissionGuarantee, FatalAbort, IntoDiagArg, LintDiagnostic, StringPart, Subdiag,
47    Subdiagnostic,
48};
49pub use diagnostic_impls::{
50    DiagArgFromDisplay, DiagSymbolList, ElidedLifetimeInPathSubdiag, ExpectedLifetimeParameter,
51    IndicateAnonymousLifetime, SingleLabelManySpans,
52};
53pub use emitter::ColorConfig;
54use emitter::{DynEmitter, Emitter, is_case_difference, is_different};
55use rustc_data_structures::AtomicRef;
56use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
57use rustc_data_structures::stable_hasher::StableHasher;
58use rustc_data_structures::sync::{DynSend, Lock};
59pub use rustc_error_messages::{
60    DiagMessage, FluentBundle, LanguageIdentifier, LazyFallbackBundle, MultiSpan, SpanLabel,
61    SubdiagMessage, fallback_fluent_bundle, fluent_bundle,
62};
63use rustc_hashes::Hash128;
64use rustc_hir::HirId;
65pub use rustc_lint_defs::{Applicability, listify, pluralize};
66use rustc_lint_defs::{Lint, LintExpectationId};
67use rustc_macros::{Decodable, Encodable};
68pub use rustc_span::ErrorGuaranteed;
69pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker};
70use rustc_span::source_map::SourceMap;
71use rustc_span::{BytePos, DUMMY_SP, Loc, Span};
72pub use snippet::Style;
73// Used by external projects such as `rust-gpu`.
74// See https://github.com/rust-lang/rust/pull/115393.
75pub use termcolor::{Color, ColorSpec, WriteColor};
76use tracing::debug;
77
78use crate::emitter::TimingEvent;
79use crate::registry::Registry;
80use crate::timings::TimingRecord;
81
82pub mod annotate_snippet_emitter_writer;
83pub mod codes;
84mod diagnostic;
85mod diagnostic_impls;
86pub mod emitter;
87pub mod error;
88pub mod json;
89mod lock;
90pub mod markdown;
91pub mod registry;
92mod snippet;
93mod styled_buffer;
94#[cfg(test)]
95mod tests;
96pub mod timings;
97pub mod translation;
98
99pub type PResult<'a, T> = Result<T, Diag<'a>>;
100
101rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
102
103// `PResult` is used a lot. Make sure it doesn't unintentionally get bigger.
104#[cfg(target_pointer_width = "64")]
105rustc_data_structures::static_assert_size!(PResult<'_, ()>, 24);
106#[cfg(target_pointer_width = "64")]
107rustc_data_structures::static_assert_size!(PResult<'_, bool>, 24);
108
109/// Used to avoid depending on `rustc_middle` in `rustc_attr_parsing`.
110/// Always the `TyCtxt`.
111pub trait LintEmitter: Copy {
112    #[track_caller]
113    fn emit_node_span_lint(
114        self,
115        lint: &'static Lint,
116        hir_id: HirId,
117        span: impl Into<MultiSpan>,
118        decorator: impl for<'a> LintDiagnostic<'a, ()>,
119    );
120}
121
122#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Encodable, Decodable)]
123pub enum SuggestionStyle {
124    /// Hide the suggested code when displaying this suggestion inline.
125    HideCodeInline,
126    /// Always hide the suggested code but display the message.
127    HideCodeAlways,
128    /// Do not display this suggestion in the cli output, it is only meant for tools.
129    CompletelyHidden,
130    /// Always show the suggested code.
131    /// This will *not* show the code if the suggestion is inline *and* the suggested code is
132    /// empty.
133    ShowCode,
134    /// Always show the suggested code independently.
135    ShowAlways,
136}
137
138impl SuggestionStyle {
139    fn hide_inline(&self) -> bool {
140        !matches!(*self, SuggestionStyle::ShowCode)
141    }
142}
143
144/// Represents the help messages seen on a diagnostic.
145#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
146pub enum Suggestions {
147    /// Indicates that new suggestions can be added or removed from this diagnostic.
148    ///
149    /// `DiagInner`'s new_* methods initialize the `suggestions` field with
150    /// this variant. Also, this is the default variant for `Suggestions`.
151    Enabled(Vec<CodeSuggestion>),
152    /// Indicates that suggestions cannot be added or removed from this diagnostic.
153    ///
154    /// Gets toggled when `.seal_suggestions()` is called on the `DiagInner`.
155    Sealed(Box<[CodeSuggestion]>),
156    /// Indicates that no suggestion is available for this diagnostic.
157    ///
158    /// Gets toggled when `.disable_suggestions()` is called on the `DiagInner`.
159    Disabled,
160}
161
162impl Suggestions {
163    /// Returns the underlying list of suggestions.
164    pub fn unwrap_tag(self) -> Vec<CodeSuggestion> {
165        match self {
166            Suggestions::Enabled(suggestions) => suggestions,
167            Suggestions::Sealed(suggestions) => suggestions.into_vec(),
168            Suggestions::Disabled => Vec::new(),
169        }
170    }
171}
172
173impl Default for Suggestions {
174    fn default() -> Self {
175        Self::Enabled(vec![])
176    }
177}
178
179#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
180pub struct CodeSuggestion {
181    /// Each substitute can have multiple variants due to multiple
182    /// applicable suggestions
183    ///
184    /// `foo.bar` might be replaced with `a.b` or `x.y` by replacing
185    /// `foo` and `bar` on their own:
186    ///
187    /// ```ignore (illustrative)
188    /// vec![
189    ///     Substitution { parts: vec![(0..3, "a"), (4..7, "b")] },
190    ///     Substitution { parts: vec![(0..3, "x"), (4..7, "y")] },
191    /// ]
192    /// ```
193    ///
194    /// or by replacing the entire span:
195    ///
196    /// ```ignore (illustrative)
197    /// vec![
198    ///     Substitution { parts: vec![(0..7, "a.b")] },
199    ///     Substitution { parts: vec![(0..7, "x.y")] },
200    /// ]
201    /// ```
202    pub substitutions: Vec<Substitution>,
203    pub msg: DiagMessage,
204    /// Visual representation of this suggestion.
205    pub style: SuggestionStyle,
206    /// Whether or not the suggestion is approximate
207    ///
208    /// Sometimes we may show suggestions with placeholders,
209    /// which are useful for users but not useful for
210    /// tools like rustfix
211    pub applicability: Applicability,
212}
213
214#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
215/// See the docs on `CodeSuggestion::substitutions`
216pub struct Substitution {
217    pub parts: Vec<SubstitutionPart>,
218}
219
220#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
221pub struct SubstitutionPart {
222    pub span: Span,
223    pub snippet: String,
224}
225
226/// Used to translate between `Span`s and byte positions within a single output line in highlighted
227/// code of structured suggestions.
228#[derive(Debug, Clone, Copy)]
229pub(crate) struct SubstitutionHighlight {
230    start: usize,
231    end: usize,
232}
233
234impl SubstitutionPart {
235    pub fn is_addition(&self, sm: &SourceMap) -> bool {
236        !self.snippet.is_empty() && !self.replaces_meaningful_content(sm)
237    }
238
239    pub fn is_deletion(&self, sm: &SourceMap) -> bool {
240        self.snippet.trim().is_empty() && self.replaces_meaningful_content(sm)
241    }
242
243    pub fn is_replacement(&self, sm: &SourceMap) -> bool {
244        !self.snippet.is_empty() && self.replaces_meaningful_content(sm)
245    }
246
247    /// Whether this is a replacement that overwrites source with a snippet
248    /// in a way that isn't a superset of the original string. For example,
249    /// replacing "abc" with "abcde" is not destructive, but replacing it
250    /// it with "abx" is, since the "c" character is lost.
251    pub fn is_destructive_replacement(&self, sm: &SourceMap) -> bool {
252        self.is_replacement(sm)
253            && !sm
254                .span_to_snippet(self.span)
255                .is_ok_and(|snippet| as_substr(snippet.trim(), self.snippet.trim()).is_some())
256    }
257
258    fn replaces_meaningful_content(&self, sm: &SourceMap) -> bool {
259        sm.span_to_snippet(self.span)
260            .map_or(!self.span.is_empty(), |snippet| !snippet.trim().is_empty())
261    }
262
263    /// Try to turn a replacement into an addition when the span that is being
264    /// overwritten matches either the prefix or suffix of the replacement.
265    fn trim_trivial_replacements(&mut self, sm: &SourceMap) {
266        if self.snippet.is_empty() {
267            return;
268        }
269        let Ok(snippet) = sm.span_to_snippet(self.span) else {
270            return;
271        };
272
273        if let Some((prefix, substr, suffix)) = as_substr(&snippet, &self.snippet) {
274            self.span = Span::new(
275                self.span.lo() + BytePos(prefix as u32),
276                self.span.hi() - BytePos(suffix as u32),
277                self.span.ctxt(),
278                self.span.parent(),
279            );
280            self.snippet = substr.to_string();
281        }
282    }
283}
284
285/// Given an original string like `AACC`, and a suggestion like `AABBCC`, try to detect
286/// the case where a substring of the suggestion is "sandwiched" in the original, like
287/// `BB` is. Return the length of the prefix, the "trimmed" suggestion, and the length
288/// of the suffix.
289fn as_substr<'a>(original: &'a str, suggestion: &'a str) -> Option<(usize, &'a str, usize)> {
290    let common_prefix = original
291        .chars()
292        .zip(suggestion.chars())
293        .take_while(|(c1, c2)| c1 == c2)
294        .map(|(c, _)| c.len_utf8())
295        .sum();
296    let original = &original[common_prefix..];
297    let suggestion = &suggestion[common_prefix..];
298    if suggestion.ends_with(original) {
299        let common_suffix = original.len();
300        Some((common_prefix, &suggestion[..suggestion.len() - original.len()], common_suffix))
301    } else {
302        None
303    }
304}
305
306impl CodeSuggestion {
307    /// Returns the assembled code suggestions, whether they should be shown with an underline
308    /// and whether the substitution only differs in capitalization.
309    pub(crate) fn splice_lines(
310        &self,
311        sm: &SourceMap,
312    ) -> Vec<(String, Vec<SubstitutionPart>, Vec<Vec<SubstitutionHighlight>>, bool)> {
313        // For the `Vec<Vec<SubstitutionHighlight>>` value, the first level of the vector
314        // corresponds to the output snippet's lines, while the second level corresponds to the
315        // substrings within that line that should be highlighted.
316
317        use rustc_span::{CharPos, Pos};
318
319        /// Extracts a substring from the provided `line_opt` based on the specified low and high
320        /// indices, appends it to the given buffer `buf`, and returns the count of newline
321        /// characters in the substring for accurate highlighting. If `line_opt` is `None`, a
322        /// newline character is appended to the buffer, and 0 is returned.
323        ///
324        /// ## Returns
325        ///
326        /// The count of newline characters in the extracted substring.
327        fn push_trailing(
328            buf: &mut String,
329            line_opt: Option<&Cow<'_, str>>,
330            lo: &Loc,
331            hi_opt: Option<&Loc>,
332        ) -> usize {
333            let mut line_count = 0;
334            // Convert CharPos to Usize, as CharPose is character offset
335            // Extract low index and high index
336            let (lo, hi_opt) = (lo.col.to_usize(), hi_opt.map(|hi| hi.col.to_usize()));
337            if let Some(line) = line_opt {
338                if let Some(lo) = line.char_indices().map(|(i, _)| i).nth(lo) {
339                    // Get high index while account for rare unicode and emoji with char_indices
340                    let hi_opt = hi_opt.and_then(|hi| line.char_indices().map(|(i, _)| i).nth(hi));
341                    match hi_opt {
342                        // If high index exist, take string from low to high index
343                        Some(hi) if hi > lo => {
344                            // count how many '\n' exist
345                            line_count = line[lo..hi].matches('\n').count();
346                            buf.push_str(&line[lo..hi])
347                        }
348                        Some(_) => (),
349                        // If high index absence, take string from low index till end string.len
350                        None => {
351                            // count how many '\n' exist
352                            line_count = line[lo..].matches('\n').count();
353                            buf.push_str(&line[lo..])
354                        }
355                    }
356                }
357                // If high index is None
358                if hi_opt.is_none() {
359                    buf.push('\n');
360                }
361            }
362            line_count
363        }
364
365        assert!(!self.substitutions.is_empty());
366
367        self.substitutions
368            .iter()
369            .filter(|subst| {
370                // Suggestions coming from macros can have malformed spans. This is a heavy
371                // handed approach to avoid ICEs by ignoring the suggestion outright.
372                let invalid = subst.parts.iter().any(|item| sm.is_valid_span(item.span).is_err());
373                if invalid {
374                    debug!("splice_lines: suggestion contains an invalid span: {:?}", subst);
375                }
376                !invalid
377            })
378            .cloned()
379            .filter_map(|mut substitution| {
380                // Assumption: all spans are in the same file, and all spans
381                // are disjoint. Sort in ascending order.
382                substitution.parts.sort_by_key(|part| part.span.lo());
383
384                // Find the bounding span.
385                let lo = substitution.parts.iter().map(|part| part.span.lo()).min()?;
386                let hi = substitution.parts.iter().map(|part| part.span.hi()).max()?;
387                let bounding_span = Span::with_root_ctxt(lo, hi);
388                // The different spans might belong to different contexts, if so ignore suggestion.
389                let lines = sm.span_to_lines(bounding_span).ok()?;
390                assert!(!lines.lines.is_empty() || bounding_span.is_dummy());
391
392                // We can't splice anything if the source is unavailable.
393                if !sm.ensure_source_file_source_present(&lines.file) {
394                    return None;
395                }
396
397                let mut highlights = vec![];
398                // To build up the result, we do this for each span:
399                // - push the line segment trailing the previous span
400                //   (at the beginning a "phantom" span pointing at the start of the line)
401                // - push lines between the previous and current span (if any)
402                // - if the previous and current span are not on the same line
403                //   push the line segment leading up to the current span
404                // - splice in the span substitution
405                //
406                // Finally push the trailing line segment of the last span
407                let sf = &lines.file;
408                let mut prev_hi = sm.lookup_char_pos(bounding_span.lo());
409                prev_hi.col = CharPos::from_usize(0);
410                let mut prev_line =
411                    lines.lines.get(0).and_then(|line0| sf.get_line(line0.line_index));
412                let mut buf = String::new();
413
414                let mut line_highlight = vec![];
415                // We need to keep track of the difference between the existing code and the added
416                // or deleted code in order to point at the correct column *after* substitution.
417                let mut acc = 0;
418                let mut only_capitalization = false;
419                for part in &mut substitution.parts {
420                    // If this is a replacement of, e.g. `"a"` into `"ab"`, adjust the
421                    // suggestion and snippet to look as if we just suggested to add
422                    // `"b"`, which is typically much easier for the user to understand.
423                    part.trim_trivial_replacements(sm);
424
425                    only_capitalization |= is_case_difference(sm, &part.snippet, part.span);
426                    let cur_lo = sm.lookup_char_pos(part.span.lo());
427                    if prev_hi.line == cur_lo.line {
428                        let mut count =
429                            push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, Some(&cur_lo));
430                        while count > 0 {
431                            highlights.push(std::mem::take(&mut line_highlight));
432                            acc = 0;
433                            count -= 1;
434                        }
435                    } else {
436                        acc = 0;
437                        highlights.push(std::mem::take(&mut line_highlight));
438                        let mut count = push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, None);
439                        while count > 0 {
440                            highlights.push(std::mem::take(&mut line_highlight));
441                            count -= 1;
442                        }
443                        // push lines between the previous and current span (if any)
444                        for idx in prev_hi.line..(cur_lo.line - 1) {
445                            if let Some(line) = sf.get_line(idx) {
446                                buf.push_str(line.as_ref());
447                                buf.push('\n');
448                                highlights.push(std::mem::take(&mut line_highlight));
449                            }
450                        }
451                        if let Some(cur_line) = sf.get_line(cur_lo.line - 1) {
452                            let end = match cur_line.char_indices().nth(cur_lo.col.to_usize()) {
453                                Some((i, _)) => i,
454                                None => cur_line.len(),
455                            };
456                            buf.push_str(&cur_line[..end]);
457                        }
458                    }
459                    // Add a whole line highlight per line in the snippet.
460                    let len: isize = part
461                        .snippet
462                        .split('\n')
463                        .next()
464                        .unwrap_or(&part.snippet)
465                        .chars()
466                        .map(|c| match c {
467                            '\t' => 4,
468                            _ => 1,
469                        })
470                        .sum();
471                    if !is_different(sm, &part.snippet, part.span) {
472                        // Account for cases where we are suggesting the same code that's already
473                        // there. This shouldn't happen often, but in some cases for multipart
474                        // suggestions it's much easier to handle it here than in the origin.
475                    } else {
476                        line_highlight.push(SubstitutionHighlight {
477                            start: (cur_lo.col.0 as isize + acc) as usize,
478                            end: (cur_lo.col.0 as isize + acc + len) as usize,
479                        });
480                    }
481                    buf.push_str(&part.snippet);
482                    let cur_hi = sm.lookup_char_pos(part.span.hi());
483                    // Account for the difference between the width of the current code and the
484                    // snippet being suggested, so that the *later* suggestions are correctly
485                    // aligned on the screen. Note that cur_hi and cur_lo can be on different
486                    // lines, so cur_hi.col can be smaller than cur_lo.col
487                    acc += len - (cur_hi.col.0 as isize - cur_lo.col.0 as isize);
488                    prev_hi = cur_hi;
489                    prev_line = sf.get_line(prev_hi.line - 1);
490                    for line in part.snippet.split('\n').skip(1) {
491                        acc = 0;
492                        highlights.push(std::mem::take(&mut line_highlight));
493                        let end: usize = line
494                            .chars()
495                            .map(|c| match c {
496                                '\t' => 4,
497                                _ => 1,
498                            })
499                            .sum();
500                        line_highlight.push(SubstitutionHighlight { start: 0, end });
501                    }
502                }
503                highlights.push(std::mem::take(&mut line_highlight));
504                // if the replacement already ends with a newline, don't print the next line
505                if !buf.ends_with('\n') {
506                    push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, None);
507                }
508                // remove trailing newlines
509                while buf.ends_with('\n') {
510                    buf.pop();
511                }
512                if highlights.iter().all(|parts| parts.is_empty()) {
513                    None
514                } else {
515                    Some((buf, substitution.parts, highlights, only_capitalization))
516                }
517            })
518            .collect()
519    }
520}
521
522/// Signifies that the compiler died with an explicit call to `.bug`
523/// or `.span_bug` rather than a failed assertion, etc.
524pub struct ExplicitBug;
525
526/// Signifies that the compiler died due to a delayed bug rather than a failed
527/// assertion, etc.
528pub struct DelayedBugPanic;
529
530/// A `DiagCtxt` deals with errors and other compiler output.
531/// Certain errors (fatal, bug, unimpl) may cause immediate exit,
532/// others log errors for later reporting.
533pub struct DiagCtxt {
534    inner: Lock<DiagCtxtInner>,
535}
536
537#[derive(Copy, Clone)]
538pub struct DiagCtxtHandle<'a> {
539    dcx: &'a DiagCtxt,
540    /// Some contexts create `DiagCtxtHandle` with this field set, and thus all
541    /// errors emitted with it will automatically taint when emitting errors.
542    tainted_with_errors: Option<&'a Cell<Option<ErrorGuaranteed>>>,
543}
544
545impl<'a> std::ops::Deref for DiagCtxtHandle<'a> {
546    type Target = &'a DiagCtxt;
547
548    fn deref(&self) -> &Self::Target {
549        &self.dcx
550    }
551}
552
553/// This inner struct exists to keep it all behind a single lock;
554/// this is done to prevent possible deadlocks in a multi-threaded compiler,
555/// as well as inconsistent state observation.
556struct DiagCtxtInner {
557    flags: DiagCtxtFlags,
558
559    registry: Registry,
560
561    /// The error guarantees from all emitted errors. The length gives the error count.
562    err_guars: Vec<ErrorGuaranteed>,
563    /// The error guarantee from all emitted lint errors. The length gives the
564    /// lint error count.
565    lint_err_guars: Vec<ErrorGuaranteed>,
566    /// The delayed bugs and their error guarantees.
567    delayed_bugs: Vec<(DelayedDiagInner, ErrorGuaranteed)>,
568
569    /// The error count shown to the user at the end.
570    deduplicated_err_count: usize,
571    /// The warning count shown to the user at the end.
572    deduplicated_warn_count: usize,
573
574    emitter: Box<DynEmitter>,
575
576    /// Must we produce a diagnostic to justify the use of the expensive
577    /// `trimmed_def_paths` function? Backtrace is the location of the call.
578    must_produce_diag: Option<Backtrace>,
579
580    /// Has this diagnostic context printed any diagnostics? (I.e. has
581    /// `self.emitter.emit_diagnostic()` been called?
582    has_printed: bool,
583
584    /// This flag indicates that an expected diagnostic was emitted and suppressed.
585    /// This is used for the `must_produce_diag` check.
586    suppressed_expected_diag: bool,
587
588    /// This set contains the code of all emitted diagnostics to avoid
589    /// emitting the same diagnostic with extended help (`--teach`) twice, which
590    /// would be unnecessary repetition.
591    taught_diagnostics: FxHashSet<ErrCode>,
592
593    /// Used to suggest rustc --explain `<error code>`
594    emitted_diagnostic_codes: FxIndexSet<ErrCode>,
595
596    /// This set contains a hash of every diagnostic that has been emitted by
597    /// this `DiagCtxt`. These hashes is used to avoid emitting the same error
598    /// twice.
599    emitted_diagnostics: FxHashSet<Hash128>,
600
601    /// Stashed diagnostics emitted in one stage of the compiler that may be
602    /// stolen and emitted/cancelled by other stages (e.g. to improve them and
603    /// add more information). All stashed diagnostics must be emitted with
604    /// `emit_stashed_diagnostics` by the time the `DiagCtxtInner` is dropped,
605    /// otherwise an assertion failure will occur.
606    stashed_diagnostics:
607        FxIndexMap<StashKey, FxIndexMap<Span, (DiagInner, Option<ErrorGuaranteed>)>>,
608
609    future_breakage_diagnostics: Vec<DiagInner>,
610
611    /// expected diagnostic will have the level `Expect` which additionally
612    /// carries the [`LintExpectationId`] of the expectation that can be
613    /// marked as fulfilled. This is a collection of all [`LintExpectationId`]s
614    /// that have been marked as fulfilled this way.
615    ///
616    /// Emitting expectations after having stolen this field can happen. In particular, an
617    /// `#[expect(warnings)]` can easily make the `UNFULFILLED_LINT_EXPECTATIONS` lint expect
618    /// itself. To avoid needless complexity in this corner case, we tolerate failing to track
619    /// those expectations.
620    ///
621    /// [RFC-2383]: https://rust-lang.github.io/rfcs/2383-lint-reasons.html
622    fulfilled_expectations: FxIndexSet<LintExpectationId>,
623
624    /// The file where the ICE information is stored. This allows delayed_span_bug backtraces to be
625    /// stored along side the main panic backtrace.
626    ice_file: Option<PathBuf>,
627}
628
629/// A key denoting where from a diagnostic was stashed.
630#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
631pub enum StashKey {
632    ItemNoType,
633    UnderscoreForArrayLengths,
634    EarlySyntaxWarning,
635    CallIntoMethod,
636    /// When an invalid lifetime e.g. `'2` should be reinterpreted
637    /// as a char literal in the parser
638    LifetimeIsChar,
639    /// Maybe there was a typo where a comma was forgotten before
640    /// FRU syntax
641    MaybeFruTypo,
642    CallAssocMethod,
643    AssociatedTypeSuggestion,
644    /// Query cycle detected, stashing in favor of a better error.
645    Cycle,
646    UndeterminedMacroResolution,
647    /// Used by `Parser::maybe_recover_trailing_expr`
648    ExprInPat,
649    /// If in the parser we detect a field expr with turbofish generic params it's possible that
650    /// it's a method call without parens. If later on in `hir_typeck` we find out that this is
651    /// the case we suppress this message and we give a better suggestion.
652    GenericInFieldExpr,
653}
654
655fn default_track_diagnostic<R>(diag: DiagInner, f: &mut dyn FnMut(DiagInner) -> R) -> R {
656    (*f)(diag)
657}
658
659/// Diagnostics emitted by `DiagCtxtInner::emit_diagnostic` are passed through this function. Used
660/// for tracking by incremental, to replay diagnostics as necessary.
661pub static TRACK_DIAGNOSTIC: AtomicRef<
662    fn(DiagInner, &mut dyn FnMut(DiagInner) -> Option<ErrorGuaranteed>) -> Option<ErrorGuaranteed>,
663> = AtomicRef::new(&(default_track_diagnostic as _));
664
665#[derive(Copy, Clone, Default)]
666pub struct DiagCtxtFlags {
667    /// If false, warning-level lints are suppressed.
668    /// (rustc: see `--allow warnings` and `--cap-lints`)
669    pub can_emit_warnings: bool,
670    /// If Some, the Nth error-level diagnostic is upgraded to bug-level.
671    /// (rustc: see `-Z treat-err-as-bug`)
672    pub treat_err_as_bug: Option<NonZero<usize>>,
673    /// Eagerly emit delayed bugs as errors, so that the compiler debugger may
674    /// see all of the errors being emitted at once.
675    pub eagerly_emit_delayed_bugs: bool,
676    /// Show macro backtraces.
677    /// (rustc: see `-Z macro-backtrace`)
678    pub macro_backtrace: bool,
679    /// If true, identical diagnostics are reported only once.
680    pub deduplicate_diagnostics: bool,
681    /// Track where errors are created. Enabled with `-Ztrack-diagnostics`.
682    pub track_diagnostics: bool,
683}
684
685impl Drop for DiagCtxtInner {
686    fn drop(&mut self) {
687        // For tools using `interface::run_compiler` (e.g. rustc, rustdoc)
688        // stashed diagnostics will have already been emitted. But for others
689        // that don't use `interface::run_compiler` (e.g. rustfmt, some clippy
690        // lints) this fallback is necessary.
691        //
692        // Important: it is sound to produce an `ErrorGuaranteed` when stashing
693        // errors because they are guaranteed to be emitted here or earlier.
694        self.emit_stashed_diagnostics();
695
696        // Important: it is sound to produce an `ErrorGuaranteed` when emitting
697        // delayed bugs because they are guaranteed to be emitted here if
698        // necessary.
699        self.flush_delayed();
700
701        // Sanity check: did we use some of the expensive `trimmed_def_paths` functions
702        // unexpectedly, that is, without producing diagnostics? If so, for debugging purposes, we
703        // suggest where this happened and how to avoid it.
704        if !self.has_printed && !self.suppressed_expected_diag && !std::thread::panicking() {
705            if let Some(backtrace) = &self.must_produce_diag {
706                let suggestion = match backtrace.status() {
707                    BacktraceStatus::Disabled => String::from(
708                        "Backtraces are currently disabled: set `RUST_BACKTRACE=1` and re-run \
709                        to see where it happened.",
710                    ),
711                    BacktraceStatus::Captured => format!(
712                        "This happened in the following `must_produce_diag` call's backtrace:\n\
713                        {backtrace}",
714                    ),
715                    _ => String::from("(impossible to capture backtrace where this happened)"),
716                };
717                panic!(
718                    "`trimmed_def_paths` called, diagnostics were expected but none were emitted. \
719                    Use `with_no_trimmed_paths` for debugging. {suggestion}"
720                );
721            }
722        }
723    }
724}
725
726impl DiagCtxt {
727    pub fn disable_warnings(mut self) -> Self {
728        self.inner.get_mut().flags.can_emit_warnings = false;
729        self
730    }
731
732    pub fn with_flags(mut self, flags: DiagCtxtFlags) -> Self {
733        self.inner.get_mut().flags = flags;
734        self
735    }
736
737    pub fn with_ice_file(mut self, ice_file: PathBuf) -> Self {
738        self.inner.get_mut().ice_file = Some(ice_file);
739        self
740    }
741
742    pub fn with_registry(mut self, registry: Registry) -> Self {
743        self.inner.get_mut().registry = registry;
744        self
745    }
746
747    pub fn new(emitter: Box<DynEmitter>) -> Self {
748        Self { inner: Lock::new(DiagCtxtInner::new(emitter)) }
749    }
750
751    pub fn make_silent(&self) {
752        let mut inner = self.inner.borrow_mut();
753        let translator = inner.emitter.translator().clone();
754        inner.emitter = Box::new(emitter::SilentEmitter { translator });
755    }
756
757    pub fn set_emitter(&self, emitter: Box<dyn Emitter + DynSend>) {
758        self.inner.borrow_mut().emitter = emitter;
759    }
760
761    /// Translate `message` eagerly with `args` to `SubdiagMessage::Eager`.
762    pub fn eagerly_translate<'a>(
763        &self,
764        message: DiagMessage,
765        args: impl Iterator<Item = DiagArg<'a>>,
766    ) -> SubdiagMessage {
767        let inner = self.inner.borrow();
768        inner.eagerly_translate(message, args)
769    }
770
771    /// Translate `message` eagerly with `args` to `String`.
772    pub fn eagerly_translate_to_string<'a>(
773        &self,
774        message: DiagMessage,
775        args: impl Iterator<Item = DiagArg<'a>>,
776    ) -> String {
777        let inner = self.inner.borrow();
778        inner.eagerly_translate_to_string(message, args)
779    }
780
781    // This is here to not allow mutation of flags;
782    // as of this writing it's used in Session::consider_optimizing and
783    // in tests in rustc_interface.
784    pub fn can_emit_warnings(&self) -> bool {
785        self.inner.borrow_mut().flags.can_emit_warnings
786    }
787
788    /// Resets the diagnostic error count as well as the cached emitted diagnostics.
789    ///
790    /// NOTE: *do not* call this function from rustc. It is only meant to be called from external
791    /// tools that want to reuse a `Parser` cleaning the previously emitted diagnostics as well as
792    /// the overall count of emitted error diagnostics.
793    pub fn reset_err_count(&self) {
794        // Use destructuring so that if a field gets added to `DiagCtxtInner`, it's impossible to
795        // fail to update this method as well.
796        let mut inner = self.inner.borrow_mut();
797        let DiagCtxtInner {
798            flags: _,
799            registry: _,
800            err_guars,
801            lint_err_guars,
802            delayed_bugs,
803            deduplicated_err_count,
804            deduplicated_warn_count,
805            emitter: _,
806            must_produce_diag,
807            has_printed,
808            suppressed_expected_diag,
809            taught_diagnostics,
810            emitted_diagnostic_codes,
811            emitted_diagnostics,
812            stashed_diagnostics,
813            future_breakage_diagnostics,
814            fulfilled_expectations,
815            ice_file: _,
816        } = inner.deref_mut();
817
818        // For the `Vec`s and `HashMap`s, we overwrite with an empty container to free the
819        // underlying memory (which `clear` would not do).
820        *err_guars = Default::default();
821        *lint_err_guars = Default::default();
822        *delayed_bugs = Default::default();
823        *deduplicated_err_count = 0;
824        *deduplicated_warn_count = 0;
825        *must_produce_diag = None;
826        *has_printed = false;
827        *suppressed_expected_diag = false;
828        *taught_diagnostics = Default::default();
829        *emitted_diagnostic_codes = Default::default();
830        *emitted_diagnostics = Default::default();
831        *stashed_diagnostics = Default::default();
832        *future_breakage_diagnostics = Default::default();
833        *fulfilled_expectations = Default::default();
834    }
835
836    pub fn handle<'a>(&'a self) -> DiagCtxtHandle<'a> {
837        DiagCtxtHandle { dcx: self, tainted_with_errors: None }
838    }
839
840    /// Link this to a taintable context so that emitting errors will automatically set
841    /// the `Option<ErrorGuaranteed>` instead of having to do that manually at every error
842    /// emission site.
843    pub fn taintable_handle<'a>(
844        &'a self,
845        tainted_with_errors: &'a Cell<Option<ErrorGuaranteed>>,
846    ) -> DiagCtxtHandle<'a> {
847        DiagCtxtHandle { dcx: self, tainted_with_errors: Some(tainted_with_errors) }
848    }
849}
850
851impl<'a> DiagCtxtHandle<'a> {
852    /// Stashes a diagnostic for possible later improvement in a different,
853    /// later stage of the compiler. Possible actions depend on the diagnostic
854    /// level:
855    /// - Level::Bug, Level:Fatal: not allowed, will trigger a panic.
856    /// - Level::Error: immediately counted as an error that has occurred, because it
857    ///   is guaranteed to be emitted eventually. Can be later accessed with the
858    ///   provided `span` and `key` through
859    ///   [`DiagCtxtHandle::try_steal_modify_and_emit_err`] or
860    ///   [`DiagCtxtHandle::try_steal_replace_and_emit_err`]. These do not allow
861    ///   cancellation or downgrading of the error. Returns
862    ///   `Some(ErrorGuaranteed)`.
863    /// - Level::DelayedBug: this does happen occasionally with errors that are
864    ///   downgraded to delayed bugs. It is not stashed, but immediately
865    ///   emitted as a delayed bug. This is because stashing it would cause it
866    ///   to be counted by `err_count` which we don't want. It doesn't matter
867    ///   that we cannot steal and improve it later, because it's not a
868    ///   user-facing error. Returns `Some(ErrorGuaranteed)` as is normal for
869    ///   delayed bugs.
870    /// - Level::Warning and lower (i.e. !is_error()): can be accessed with the
871    ///   provided `span` and `key` through [`DiagCtxtHandle::steal_non_err()`]. This
872    ///   allows cancelling and downgrading of the diagnostic. Returns `None`.
873    pub fn stash_diagnostic(
874        &self,
875        span: Span,
876        key: StashKey,
877        diag: DiagInner,
878    ) -> Option<ErrorGuaranteed> {
879        let guar = match diag.level {
880            Bug | Fatal => {
881                self.span_bug(
882                    span,
883                    format!("invalid level in `stash_diagnostic`: {:?}", diag.level),
884                );
885            }
886            // We delay a bug here so that `-Ztreat-err-as-bug -Zeagerly-emit-delayed-bugs`
887            // can be used to create a backtrace at the stashing site instead of whenever the
888            // diagnostic context is dropped and thus delayed bugs are emitted.
889            Error => Some(self.span_delayed_bug(span, format!("stashing {key:?}"))),
890            DelayedBug => {
891                return self.inner.borrow_mut().emit_diagnostic(diag, self.tainted_with_errors);
892            }
893            ForceWarning | Warning | Note | OnceNote | Help | OnceHelp | FailureNote | Allow
894            | Expect => None,
895        };
896
897        // FIXME(Centril, #69537): Consider reintroducing panic on overwriting a stashed diagnostic
898        // if/when we have a more robust macro-friendly replacement for `(span, key)` as a key.
899        // See the PR for a discussion.
900        self.inner
901            .borrow_mut()
902            .stashed_diagnostics
903            .entry(key)
904            .or_default()
905            .insert(span.with_parent(None), (diag, guar));
906
907        guar
908    }
909
910    /// Steal a previously stashed non-error diagnostic with the given `Span`
911    /// and [`StashKey`] as the key. Panics if the found diagnostic is an
912    /// error.
913    pub fn steal_non_err(self, span: Span, key: StashKey) -> Option<Diag<'a, ()>> {
914        // FIXME(#120456) - is `swap_remove` correct?
915        let (diag, guar) = self.inner.borrow_mut().stashed_diagnostics.get_mut(&key).and_then(
916            |stashed_diagnostics| stashed_diagnostics.swap_remove(&span.with_parent(None)),
917        )?;
918        assert!(!diag.is_error());
919        assert!(guar.is_none());
920        Some(Diag::new_diagnostic(self, diag))
921    }
922
923    /// Steals a previously stashed error with the given `Span` and
924    /// [`StashKey`] as the key, modifies it, and emits it. Returns `None` if
925    /// no matching diagnostic is found. Panics if the found diagnostic's level
926    /// isn't `Level::Error`.
927    pub fn try_steal_modify_and_emit_err<F>(
928        self,
929        span: Span,
930        key: StashKey,
931        mut modify_err: F,
932    ) -> Option<ErrorGuaranteed>
933    where
934        F: FnMut(&mut Diag<'_>),
935    {
936        // FIXME(#120456) - is `swap_remove` correct?
937        let err = self.inner.borrow_mut().stashed_diagnostics.get_mut(&key).and_then(
938            |stashed_diagnostics| stashed_diagnostics.swap_remove(&span.with_parent(None)),
939        );
940        err.map(|(err, guar)| {
941            // The use of `::<ErrorGuaranteed>` is safe because level is `Level::Error`.
942            assert_eq!(err.level, Error);
943            assert!(guar.is_some());
944            let mut err = Diag::<ErrorGuaranteed>::new_diagnostic(self, err);
945            modify_err(&mut err);
946            assert_eq!(err.level, Error);
947            err.emit()
948        })
949    }
950
951    /// Steals a previously stashed error with the given `Span` and
952    /// [`StashKey`] as the key, cancels it if found, and emits `new_err`.
953    /// Panics if the found diagnostic's level isn't `Level::Error`.
954    pub fn try_steal_replace_and_emit_err(
955        self,
956        span: Span,
957        key: StashKey,
958        new_err: Diag<'_>,
959    ) -> ErrorGuaranteed {
960        // FIXME(#120456) - is `swap_remove` correct?
961        let old_err = self.inner.borrow_mut().stashed_diagnostics.get_mut(&key).and_then(
962            |stashed_diagnostics| stashed_diagnostics.swap_remove(&span.with_parent(None)),
963        );
964        match old_err {
965            Some((old_err, guar)) => {
966                assert_eq!(old_err.level, Error);
967                assert!(guar.is_some());
968                // Because `old_err` has already been counted, it can only be
969                // safely cancelled because the `new_err` supplants it.
970                Diag::<ErrorGuaranteed>::new_diagnostic(self, old_err).cancel();
971            }
972            None => {}
973        };
974        new_err.emit()
975    }
976
977    pub fn has_stashed_diagnostic(&self, span: Span, key: StashKey) -> bool {
978        let inner = self.inner.borrow();
979        if let Some(stashed_diagnostics) = inner.stashed_diagnostics.get(&key)
980            && !stashed_diagnostics.is_empty()
981        {
982            stashed_diagnostics.contains_key(&span.with_parent(None))
983        } else {
984            false
985        }
986    }
987
988    /// Emit all stashed diagnostics.
989    pub fn emit_stashed_diagnostics(&self) -> Option<ErrorGuaranteed> {
990        self.inner.borrow_mut().emit_stashed_diagnostics()
991    }
992
993    /// This excludes delayed bugs.
994    #[inline]
995    pub fn err_count(&self) -> usize {
996        let inner = self.inner.borrow();
997        inner.err_guars.len()
998            + inner.lint_err_guars.len()
999            + inner
1000                .stashed_diagnostics
1001                .values()
1002                .map(|a| a.values().filter(|(_, guar)| guar.is_some()).count())
1003                .sum::<usize>()
1004    }
1005
1006    /// This excludes lint errors and delayed bugs. Unless absolutely
1007    /// necessary, prefer `has_errors` to this method.
1008    pub fn has_errors_excluding_lint_errors(&self) -> Option<ErrorGuaranteed> {
1009        self.inner.borrow().has_errors_excluding_lint_errors()
1010    }
1011
1012    /// This excludes delayed bugs.
1013    pub fn has_errors(&self) -> Option<ErrorGuaranteed> {
1014        self.inner.borrow().has_errors()
1015    }
1016
1017    /// This excludes nothing. Unless absolutely necessary, prefer `has_errors`
1018    /// to this method.
1019    pub fn has_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> {
1020        self.inner.borrow().has_errors_or_delayed_bugs()
1021    }
1022
1023    pub fn print_error_count(&self) {
1024        let mut inner = self.inner.borrow_mut();
1025
1026        // Any stashed diagnostics should have been handled by
1027        // `emit_stashed_diagnostics` by now.
1028        assert!(inner.stashed_diagnostics.is_empty());
1029
1030        if inner.treat_err_as_bug() {
1031            return;
1032        }
1033
1034        let warnings = match inner.deduplicated_warn_count {
1035            0 => Cow::from(""),
1036            1 => Cow::from("1 warning emitted"),
1037            count => Cow::from(format!("{count} warnings emitted")),
1038        };
1039        let errors = match inner.deduplicated_err_count {
1040            0 => Cow::from(""),
1041            1 => Cow::from("aborting due to 1 previous error"),
1042            count => Cow::from(format!("aborting due to {count} previous errors")),
1043        };
1044
1045        match (errors.len(), warnings.len()) {
1046            (0, 0) => return,
1047            (0, _) => {
1048                // Use `ForceWarning` rather than `Warning` to guarantee emission, e.g. with a
1049                // configuration like `--cap-lints allow --force-warn bare_trait_objects`.
1050                inner.emit_diagnostic(
1051                    DiagInner::new(ForceWarning, DiagMessage::Str(warnings)),
1052                    None,
1053                );
1054            }
1055            (_, 0) => {
1056                inner.emit_diagnostic(DiagInner::new(Error, errors), self.tainted_with_errors);
1057            }
1058            (_, _) => {
1059                inner.emit_diagnostic(
1060                    DiagInner::new(Error, format!("{errors}; {warnings}")),
1061                    self.tainted_with_errors,
1062                );
1063            }
1064        }
1065
1066        let can_show_explain = inner.emitter.should_show_explain();
1067        let are_there_diagnostics = !inner.emitted_diagnostic_codes.is_empty();
1068        if can_show_explain && are_there_diagnostics {
1069            let mut error_codes = inner
1070                .emitted_diagnostic_codes
1071                .iter()
1072                .filter_map(|&code| {
1073                    if inner.registry.try_find_description(code).is_ok() {
1074                        Some(code.to_string())
1075                    } else {
1076                        None
1077                    }
1078                })
1079                .collect::<Vec<_>>();
1080            if !error_codes.is_empty() {
1081                error_codes.sort();
1082                if error_codes.len() > 1 {
1083                    let limit = if error_codes.len() > 9 { 9 } else { error_codes.len() };
1084                    let msg1 = format!(
1085                        "Some errors have detailed explanations: {}{}",
1086                        error_codes[..limit].join(", "),
1087                        if error_codes.len() > 9 { "..." } else { "." }
1088                    );
1089                    let msg2 = format!(
1090                        "For more information about an error, try `rustc --explain {}`.",
1091                        &error_codes[0]
1092                    );
1093                    inner.emit_diagnostic(DiagInner::new(FailureNote, msg1), None);
1094                    inner.emit_diagnostic(DiagInner::new(FailureNote, msg2), None);
1095                } else {
1096                    let msg = format!(
1097                        "For more information about this error, try `rustc --explain {}`.",
1098                        &error_codes[0]
1099                    );
1100                    inner.emit_diagnostic(DiagInner::new(FailureNote, msg), None);
1101                }
1102            }
1103        }
1104    }
1105
1106    /// This excludes delayed bugs. Used for early aborts after errors occurred
1107    /// -- e.g. because continuing in the face of errors is likely to lead to
1108    /// bad results, such as spurious/uninteresting additional errors -- when
1109    /// returning an error `Result` is difficult.
1110    pub fn abort_if_errors(&self) {
1111        if let Some(guar) = self.has_errors() {
1112            guar.raise_fatal();
1113        }
1114    }
1115
1116    /// `true` if we haven't taught a diagnostic with this code already.
1117    /// The caller must then teach the user about such a diagnostic.
1118    ///
1119    /// Used to suppress emitting the same error multiple times with extended explanation when
1120    /// calling `-Zteach`.
1121    pub fn must_teach(&self, code: ErrCode) -> bool {
1122        self.inner.borrow_mut().taught_diagnostics.insert(code)
1123    }
1124
1125    pub fn emit_diagnostic(&self, diagnostic: DiagInner) -> Option<ErrorGuaranteed> {
1126        self.inner.borrow_mut().emit_diagnostic(diagnostic, self.tainted_with_errors)
1127    }
1128
1129    pub fn emit_artifact_notification(&self, path: &Path, artifact_type: &str) {
1130        self.inner.borrow_mut().emitter.emit_artifact_notification(path, artifact_type);
1131    }
1132
1133    pub fn emit_timing_section_start(&self, record: TimingRecord) {
1134        self.inner.borrow_mut().emitter.emit_timing_section(record, TimingEvent::Start);
1135    }
1136
1137    pub fn emit_timing_section_end(&self, record: TimingRecord) {
1138        self.inner.borrow_mut().emitter.emit_timing_section(record, TimingEvent::End);
1139    }
1140
1141    pub fn emit_future_breakage_report(&self) {
1142        let inner = &mut *self.inner.borrow_mut();
1143        let diags = std::mem::take(&mut inner.future_breakage_diagnostics);
1144        if !diags.is_empty() {
1145            inner.emitter.emit_future_breakage_report(diags, &inner.registry);
1146        }
1147    }
1148
1149    pub fn emit_unused_externs(
1150        &self,
1151        lint_level: rustc_lint_defs::Level,
1152        loud: bool,
1153        unused_externs: &[&str],
1154    ) {
1155        let mut inner = self.inner.borrow_mut();
1156
1157        // This "error" is an odd duck.
1158        // - It's only produce with JSON output.
1159        // - It's not emitted the usual way, via `emit_diagnostic`.
1160        // - The `$message_type` field is "unused_externs" rather than the usual
1161        //   "diagnosic".
1162        //
1163        // We count it as a lint error because it has a lint level. The value
1164        // of `loud` (which comes from "unused-externs" or
1165        // "unused-externs-silent"), also affects whether it's treated like a
1166        // hard error or not.
1167        if loud && lint_level.is_error() {
1168            // This `unchecked_error_guaranteed` is valid. It is where the
1169            // `ErrorGuaranteed` for unused_extern errors originates.
1170            #[allow(deprecated)]
1171            inner.lint_err_guars.push(ErrorGuaranteed::unchecked_error_guaranteed());
1172            inner.panic_if_treat_err_as_bug();
1173        }
1174
1175        inner.emitter.emit_unused_externs(lint_level, unused_externs)
1176    }
1177
1178    /// This methods steals all [`LintExpectationId`]s that are stored inside
1179    /// [`DiagCtxtInner`] and indicate that the linked expectation has been fulfilled.
1180    #[must_use]
1181    pub fn steal_fulfilled_expectation_ids(&self) -> FxIndexSet<LintExpectationId> {
1182        std::mem::take(&mut self.inner.borrow_mut().fulfilled_expectations)
1183    }
1184
1185    pub fn flush_delayed(&self) {
1186        self.inner.borrow_mut().flush_delayed();
1187    }
1188
1189    /// Used when trimmed_def_paths is called and we must produce a diagnostic
1190    /// to justify its cost.
1191    #[track_caller]
1192    pub fn set_must_produce_diag(&self) {
1193        assert!(
1194            self.inner.borrow().must_produce_diag.is_none(),
1195            "should only need to collect a backtrace once"
1196        );
1197        self.inner.borrow_mut().must_produce_diag = Some(Backtrace::capture());
1198    }
1199}
1200
1201// This `impl` block contains only the public diagnostic creation/emission API.
1202//
1203// Functions beginning with `struct_`/`create_` create a diagnostic. Other
1204// functions create and emit a diagnostic all in one go.
1205impl<'a> DiagCtxtHandle<'a> {
1206    // No `#[rustc_lint_diagnostics]` and no `impl Into<DiagMessage>` because bug messages aren't
1207    // user-facing.
1208    #[track_caller]
1209    pub fn struct_bug(self, msg: impl Into<Cow<'static, str>>) -> Diag<'a, BugAbort> {
1210        Diag::new(self, Bug, msg.into())
1211    }
1212
1213    // No `#[rustc_lint_diagnostics]` and no `impl Into<DiagMessage>` because bug messages aren't
1214    // user-facing.
1215    #[track_caller]
1216    pub fn bug(self, msg: impl Into<Cow<'static, str>>) -> ! {
1217        self.struct_bug(msg).emit()
1218    }
1219
1220    // No `#[rustc_lint_diagnostics]` and no `impl Into<DiagMessage>` because bug messages aren't
1221    // user-facing.
1222    #[track_caller]
1223    pub fn struct_span_bug(
1224        self,
1225        span: impl Into<MultiSpan>,
1226        msg: impl Into<Cow<'static, str>>,
1227    ) -> Diag<'a, BugAbort> {
1228        self.struct_bug(msg).with_span(span)
1229    }
1230
1231    // No `#[rustc_lint_diagnostics]` and no `impl Into<DiagMessage>` because bug messages aren't
1232    // user-facing.
1233    #[track_caller]
1234    pub fn span_bug(self, span: impl Into<MultiSpan>, msg: impl Into<Cow<'static, str>>) -> ! {
1235        self.struct_span_bug(span, msg.into()).emit()
1236    }
1237
1238    #[track_caller]
1239    pub fn create_bug(self, bug: impl Diagnostic<'a, BugAbort>) -> Diag<'a, BugAbort> {
1240        bug.into_diag(self, Bug)
1241    }
1242
1243    #[track_caller]
1244    pub fn emit_bug(self, bug: impl Diagnostic<'a, BugAbort>) -> ! {
1245        self.create_bug(bug).emit()
1246    }
1247
1248    #[rustc_lint_diagnostics]
1249    #[track_caller]
1250    pub fn struct_fatal(self, msg: impl Into<DiagMessage>) -> Diag<'a, FatalAbort> {
1251        Diag::new(self, Fatal, msg)
1252    }
1253
1254    #[rustc_lint_diagnostics]
1255    #[track_caller]
1256    pub fn fatal(self, msg: impl Into<DiagMessage>) -> ! {
1257        self.struct_fatal(msg).emit()
1258    }
1259
1260    #[rustc_lint_diagnostics]
1261    #[track_caller]
1262    pub fn struct_span_fatal(
1263        self,
1264        span: impl Into<MultiSpan>,
1265        msg: impl Into<DiagMessage>,
1266    ) -> Diag<'a, FatalAbort> {
1267        self.struct_fatal(msg).with_span(span)
1268    }
1269
1270    #[rustc_lint_diagnostics]
1271    #[track_caller]
1272    pub fn span_fatal(self, span: impl Into<MultiSpan>, msg: impl Into<DiagMessage>) -> ! {
1273        self.struct_span_fatal(span, msg).emit()
1274    }
1275
1276    #[track_caller]
1277    pub fn create_fatal(self, fatal: impl Diagnostic<'a, FatalAbort>) -> Diag<'a, FatalAbort> {
1278        fatal.into_diag(self, Fatal)
1279    }
1280
1281    #[track_caller]
1282    pub fn emit_fatal(self, fatal: impl Diagnostic<'a, FatalAbort>) -> ! {
1283        self.create_fatal(fatal).emit()
1284    }
1285
1286    #[track_caller]
1287    pub fn create_almost_fatal(
1288        self,
1289        fatal: impl Diagnostic<'a, FatalError>,
1290    ) -> Diag<'a, FatalError> {
1291        fatal.into_diag(self, Fatal)
1292    }
1293
1294    #[track_caller]
1295    pub fn emit_almost_fatal(self, fatal: impl Diagnostic<'a, FatalError>) -> FatalError {
1296        self.create_almost_fatal(fatal).emit()
1297    }
1298
1299    // FIXME: This method should be removed (every error should have an associated error code).
1300    #[rustc_lint_diagnostics]
1301    #[track_caller]
1302    pub fn struct_err(self, msg: impl Into<DiagMessage>) -> Diag<'a> {
1303        Diag::new(self, Error, msg)
1304    }
1305
1306    #[rustc_lint_diagnostics]
1307    #[track_caller]
1308    pub fn err(self, msg: impl Into<DiagMessage>) -> ErrorGuaranteed {
1309        self.struct_err(msg).emit()
1310    }
1311
1312    #[rustc_lint_diagnostics]
1313    #[track_caller]
1314    pub fn struct_span_err(
1315        self,
1316        span: impl Into<MultiSpan>,
1317        msg: impl Into<DiagMessage>,
1318    ) -> Diag<'a> {
1319        self.struct_err(msg).with_span(span)
1320    }
1321
1322    #[rustc_lint_diagnostics]
1323    #[track_caller]
1324    pub fn span_err(
1325        self,
1326        span: impl Into<MultiSpan>,
1327        msg: impl Into<DiagMessage>,
1328    ) -> ErrorGuaranteed {
1329        self.struct_span_err(span, msg).emit()
1330    }
1331
1332    #[track_caller]
1333    pub fn create_err(self, err: impl Diagnostic<'a>) -> Diag<'a> {
1334        err.into_diag(self, Error)
1335    }
1336
1337    #[track_caller]
1338    pub fn emit_err(self, err: impl Diagnostic<'a>) -> ErrorGuaranteed {
1339        self.create_err(err).emit()
1340    }
1341
1342    /// Ensures that an error is printed. See `Level::DelayedBug`.
1343    //
1344    // No `#[rustc_lint_diagnostics]` and no `impl Into<DiagMessage>` because bug messages aren't
1345    // user-facing.
1346    #[track_caller]
1347    pub fn delayed_bug(self, msg: impl Into<Cow<'static, str>>) -> ErrorGuaranteed {
1348        Diag::<ErrorGuaranteed>::new(self, DelayedBug, msg.into()).emit()
1349    }
1350
1351    /// Ensures that an error is printed. See [`Level::DelayedBug`].
1352    ///
1353    /// Note: this function used to be called `delay_span_bug`. It was renamed
1354    /// to match similar functions like `span_err`, `span_warn`, etc.
1355    //
1356    // No `#[rustc_lint_diagnostics]` and no `impl Into<DiagMessage>` because bug messages aren't
1357    // user-facing.
1358    #[track_caller]
1359    pub fn span_delayed_bug(
1360        self,
1361        sp: impl Into<MultiSpan>,
1362        msg: impl Into<Cow<'static, str>>,
1363    ) -> ErrorGuaranteed {
1364        Diag::<ErrorGuaranteed>::new(self, DelayedBug, msg.into()).with_span(sp).emit()
1365    }
1366
1367    #[rustc_lint_diagnostics]
1368    #[track_caller]
1369    pub fn struct_warn(self, msg: impl Into<DiagMessage>) -> Diag<'a, ()> {
1370        Diag::new(self, Warning, msg)
1371    }
1372
1373    #[rustc_lint_diagnostics]
1374    #[track_caller]
1375    pub fn warn(self, msg: impl Into<DiagMessage>) {
1376        self.struct_warn(msg).emit()
1377    }
1378
1379    #[rustc_lint_diagnostics]
1380    #[track_caller]
1381    pub fn struct_span_warn(
1382        self,
1383        span: impl Into<MultiSpan>,
1384        msg: impl Into<DiagMessage>,
1385    ) -> Diag<'a, ()> {
1386        self.struct_warn(msg).with_span(span)
1387    }
1388
1389    #[rustc_lint_diagnostics]
1390    #[track_caller]
1391    pub fn span_warn(self, span: impl Into<MultiSpan>, msg: impl Into<DiagMessage>) {
1392        self.struct_span_warn(span, msg).emit()
1393    }
1394
1395    #[track_caller]
1396    pub fn create_warn(self, warning: impl Diagnostic<'a, ()>) -> Diag<'a, ()> {
1397        warning.into_diag(self, Warning)
1398    }
1399
1400    #[track_caller]
1401    pub fn emit_warn(self, warning: impl Diagnostic<'a, ()>) {
1402        self.create_warn(warning).emit()
1403    }
1404
1405    #[rustc_lint_diagnostics]
1406    #[track_caller]
1407    pub fn struct_note(self, msg: impl Into<DiagMessage>) -> Diag<'a, ()> {
1408        Diag::new(self, Note, msg)
1409    }
1410
1411    #[rustc_lint_diagnostics]
1412    #[track_caller]
1413    pub fn note(&self, msg: impl Into<DiagMessage>) {
1414        self.struct_note(msg).emit()
1415    }
1416
1417    #[rustc_lint_diagnostics]
1418    #[track_caller]
1419    pub fn struct_span_note(
1420        self,
1421        span: impl Into<MultiSpan>,
1422        msg: impl Into<DiagMessage>,
1423    ) -> Diag<'a, ()> {
1424        self.struct_note(msg).with_span(span)
1425    }
1426
1427    #[rustc_lint_diagnostics]
1428    #[track_caller]
1429    pub fn span_note(self, span: impl Into<MultiSpan>, msg: impl Into<DiagMessage>) {
1430        self.struct_span_note(span, msg).emit()
1431    }
1432
1433    #[track_caller]
1434    pub fn create_note(self, note: impl Diagnostic<'a, ()>) -> Diag<'a, ()> {
1435        note.into_diag(self, Note)
1436    }
1437
1438    #[track_caller]
1439    pub fn emit_note(self, note: impl Diagnostic<'a, ()>) {
1440        self.create_note(note).emit()
1441    }
1442
1443    #[rustc_lint_diagnostics]
1444    #[track_caller]
1445    pub fn struct_help(self, msg: impl Into<DiagMessage>) -> Diag<'a, ()> {
1446        Diag::new(self, Help, msg)
1447    }
1448
1449    #[rustc_lint_diagnostics]
1450    #[track_caller]
1451    pub fn struct_failure_note(self, msg: impl Into<DiagMessage>) -> Diag<'a, ()> {
1452        Diag::new(self, FailureNote, msg)
1453    }
1454
1455    #[rustc_lint_diagnostics]
1456    #[track_caller]
1457    pub fn struct_allow(self, msg: impl Into<DiagMessage>) -> Diag<'a, ()> {
1458        Diag::new(self, Allow, msg)
1459    }
1460
1461    #[rustc_lint_diagnostics]
1462    #[track_caller]
1463    pub fn struct_expect(self, msg: impl Into<DiagMessage>, id: LintExpectationId) -> Diag<'a, ()> {
1464        Diag::new(self, Expect, msg).with_lint_id(id)
1465    }
1466}
1467
1468// Note: we prefer implementing operations on `DiagCtxt`, rather than
1469// `DiagCtxtInner`, whenever possible. This minimizes functions where
1470// `DiagCtxt::foo()` just borrows `inner` and forwards a call to
1471// `DiagCtxtInner::foo`.
1472impl DiagCtxtInner {
1473    fn new(emitter: Box<DynEmitter>) -> Self {
1474        Self {
1475            flags: DiagCtxtFlags { can_emit_warnings: true, ..Default::default() },
1476            registry: Registry::new(&[]),
1477            err_guars: Vec::new(),
1478            lint_err_guars: Vec::new(),
1479            delayed_bugs: Vec::new(),
1480            deduplicated_err_count: 0,
1481            deduplicated_warn_count: 0,
1482            emitter,
1483            must_produce_diag: None,
1484            has_printed: false,
1485            suppressed_expected_diag: false,
1486            taught_diagnostics: Default::default(),
1487            emitted_diagnostic_codes: Default::default(),
1488            emitted_diagnostics: Default::default(),
1489            stashed_diagnostics: Default::default(),
1490            future_breakage_diagnostics: Vec::new(),
1491            fulfilled_expectations: Default::default(),
1492            ice_file: None,
1493        }
1494    }
1495
1496    /// Emit all stashed diagnostics.
1497    fn emit_stashed_diagnostics(&mut self) -> Option<ErrorGuaranteed> {
1498        let mut guar = None;
1499        let has_errors = !self.err_guars.is_empty();
1500        for (_, stashed_diagnostics) in std::mem::take(&mut self.stashed_diagnostics).into_iter() {
1501            for (_, (diag, _guar)) in stashed_diagnostics {
1502                if !diag.is_error() {
1503                    // Unless they're forced, don't flush stashed warnings when
1504                    // there are errors, to avoid causing warning overload. The
1505                    // stash would've been stolen already if it were important.
1506                    if !diag.is_force_warn() && has_errors {
1507                        continue;
1508                    }
1509                }
1510                guar = guar.or(self.emit_diagnostic(diag, None));
1511            }
1512        }
1513        guar
1514    }
1515
1516    // Return value is only `Some` if the level is `Error` or `DelayedBug`.
1517    fn emit_diagnostic(
1518        &mut self,
1519        mut diagnostic: DiagInner,
1520        taint: Option<&Cell<Option<ErrorGuaranteed>>>,
1521    ) -> Option<ErrorGuaranteed> {
1522        if diagnostic.has_future_breakage() {
1523            // Future breakages aren't emitted if they're `Level::Allow` or
1524            // `Level::Expect`, but they still need to be constructed and
1525            // stashed below, so they'll trigger the must_produce_diag check.
1526            assert_matches!(diagnostic.level, Error | ForceWarning | Warning | Allow | Expect);
1527            self.future_breakage_diagnostics.push(diagnostic.clone());
1528        }
1529
1530        // We call TRACK_DIAGNOSTIC with an empty closure for the cases that
1531        // return early *and* have some kind of side-effect, except where
1532        // noted.
1533        match diagnostic.level {
1534            Bug => {}
1535            Fatal | Error => {
1536                if self.treat_next_err_as_bug() {
1537                    // `Fatal` and `Error` can be promoted to `Bug`.
1538                    diagnostic.level = Bug;
1539                }
1540            }
1541            DelayedBug => {
1542                // Note that because we check these conditions first,
1543                // `-Zeagerly-emit-delayed-bugs` and `-Ztreat-err-as-bug`
1544                // continue to work even after we've issued an error and
1545                // stopped recording new delayed bugs.
1546                if self.flags.eagerly_emit_delayed_bugs {
1547                    // `DelayedBug` can be promoted to `Error` or `Bug`.
1548                    if self.treat_next_err_as_bug() {
1549                        diagnostic.level = Bug;
1550                    } else {
1551                        diagnostic.level = Error;
1552                    }
1553                } else {
1554                    // If we have already emitted at least one error, we don't need
1555                    // to record the delayed bug, because it'll never be used.
1556                    return if let Some(guar) = self.has_errors() {
1557                        Some(guar)
1558                    } else {
1559                        // No `TRACK_DIAGNOSTIC` call is needed, because the
1560                        // incremental session is deleted if there is a delayed
1561                        // bug. This also saves us from cloning the diagnostic.
1562                        let backtrace = std::backtrace::Backtrace::capture();
1563                        // This `unchecked_error_guaranteed` is valid. It is where the
1564                        // `ErrorGuaranteed` for delayed bugs originates. See
1565                        // `DiagCtxtInner::drop`.
1566                        #[allow(deprecated)]
1567                        let guar = ErrorGuaranteed::unchecked_error_guaranteed();
1568                        self.delayed_bugs
1569                            .push((DelayedDiagInner::with_backtrace(diagnostic, backtrace), guar));
1570                        Some(guar)
1571                    };
1572                }
1573            }
1574            ForceWarning if diagnostic.lint_id.is_none() => {} // `ForceWarning(Some(...))` is below, with `Expect`
1575            Warning => {
1576                if !self.flags.can_emit_warnings {
1577                    // We are not emitting warnings.
1578                    if diagnostic.has_future_breakage() {
1579                        // The side-effect is at the top of this method.
1580                        TRACK_DIAGNOSTIC(diagnostic, &mut |_| None);
1581                    }
1582                    return None;
1583                }
1584            }
1585            Note | Help | FailureNote => {}
1586            OnceNote | OnceHelp => panic!("bad level: {:?}", diagnostic.level),
1587            Allow => {
1588                // Nothing emitted for allowed lints.
1589                if diagnostic.has_future_breakage() {
1590                    // The side-effect is at the top of this method.
1591                    TRACK_DIAGNOSTIC(diagnostic, &mut |_| None);
1592                    self.suppressed_expected_diag = true;
1593                }
1594                return None;
1595            }
1596            Expect | ForceWarning => {
1597                self.fulfilled_expectations.insert(diagnostic.lint_id.unwrap());
1598                if let Expect = diagnostic.level {
1599                    // Nothing emitted here for expected lints.
1600                    TRACK_DIAGNOSTIC(diagnostic, &mut |_| None);
1601                    self.suppressed_expected_diag = true;
1602                    return None;
1603                }
1604            }
1605        }
1606
1607        TRACK_DIAGNOSTIC(diagnostic, &mut |mut diagnostic| {
1608            if let Some(code) = diagnostic.code {
1609                self.emitted_diagnostic_codes.insert(code);
1610            }
1611
1612            let already_emitted = {
1613                let mut hasher = StableHasher::new();
1614                diagnostic.hash(&mut hasher);
1615                let diagnostic_hash = hasher.finish();
1616                !self.emitted_diagnostics.insert(diagnostic_hash)
1617            };
1618
1619            let is_error = diagnostic.is_error();
1620            let is_lint = diagnostic.is_lint.is_some();
1621
1622            // Only emit the diagnostic if we've been asked to deduplicate or
1623            // haven't already emitted an equivalent diagnostic.
1624            if !(self.flags.deduplicate_diagnostics && already_emitted) {
1625                debug!(?diagnostic);
1626                debug!(?self.emitted_diagnostics);
1627
1628                let not_yet_emitted = |sub: &mut Subdiag| {
1629                    debug!(?sub);
1630                    if sub.level != OnceNote && sub.level != OnceHelp {
1631                        return true;
1632                    }
1633                    let mut hasher = StableHasher::new();
1634                    sub.hash(&mut hasher);
1635                    let diagnostic_hash = hasher.finish();
1636                    debug!(?diagnostic_hash);
1637                    self.emitted_diagnostics.insert(diagnostic_hash)
1638                };
1639                diagnostic.children.retain_mut(not_yet_emitted);
1640                if already_emitted {
1641                    let msg = "duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`";
1642                    diagnostic.sub(Note, msg, MultiSpan::new());
1643                }
1644
1645                if is_error {
1646                    self.deduplicated_err_count += 1;
1647                } else if matches!(diagnostic.level, ForceWarning | Warning) {
1648                    self.deduplicated_warn_count += 1;
1649                }
1650                self.has_printed = true;
1651
1652                self.emitter.emit_diagnostic(diagnostic, &self.registry);
1653            }
1654
1655            if is_error {
1656                // If we have any delayed bugs recorded, we can discard them
1657                // because they won't be used. (This should only occur if there
1658                // have been no errors previously emitted, because we don't add
1659                // new delayed bugs once the first error is emitted.)
1660                if !self.delayed_bugs.is_empty() {
1661                    assert_eq!(self.lint_err_guars.len() + self.err_guars.len(), 0);
1662                    self.delayed_bugs.clear();
1663                    self.delayed_bugs.shrink_to_fit();
1664                }
1665
1666                // This `unchecked_error_guaranteed` is valid. It is where the
1667                // `ErrorGuaranteed` for errors and lint errors originates.
1668                #[allow(deprecated)]
1669                let guar = ErrorGuaranteed::unchecked_error_guaranteed();
1670                if is_lint {
1671                    self.lint_err_guars.push(guar);
1672                } else {
1673                    if let Some(taint) = taint {
1674                        taint.set(Some(guar));
1675                    }
1676                    self.err_guars.push(guar);
1677                }
1678                self.panic_if_treat_err_as_bug();
1679                Some(guar)
1680            } else {
1681                None
1682            }
1683        })
1684    }
1685
1686    fn treat_err_as_bug(&self) -> bool {
1687        self.flags
1688            .treat_err_as_bug
1689            .is_some_and(|c| self.err_guars.len() + self.lint_err_guars.len() >= c.get())
1690    }
1691
1692    // Use this one before incrementing `err_count`.
1693    fn treat_next_err_as_bug(&self) -> bool {
1694        self.flags
1695            .treat_err_as_bug
1696            .is_some_and(|c| self.err_guars.len() + self.lint_err_guars.len() + 1 >= c.get())
1697    }
1698
1699    fn has_errors_excluding_lint_errors(&self) -> Option<ErrorGuaranteed> {
1700        self.err_guars.get(0).copied().or_else(|| {
1701            if let Some((_diag, guar)) = self
1702                .stashed_diagnostics
1703                .values()
1704                .flat_map(|stashed_diagnostics| stashed_diagnostics.values())
1705                .find(|(diag, guar)| guar.is_some() && diag.is_lint.is_none())
1706            {
1707                *guar
1708            } else {
1709                None
1710            }
1711        })
1712    }
1713
1714    fn has_errors(&self) -> Option<ErrorGuaranteed> {
1715        self.err_guars.get(0).copied().or_else(|| self.lint_err_guars.get(0).copied()).or_else(
1716            || {
1717                self.stashed_diagnostics.values().find_map(|stashed_diagnostics| {
1718                    stashed_diagnostics.values().find_map(|(_, guar)| *guar)
1719                })
1720            },
1721        )
1722    }
1723
1724    fn has_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> {
1725        self.has_errors().or_else(|| self.delayed_bugs.get(0).map(|(_, guar)| guar).copied())
1726    }
1727
1728    /// Translate `message` eagerly with `args` to `SubdiagMessage::Eager`.
1729    fn eagerly_translate<'a>(
1730        &self,
1731        message: DiagMessage,
1732        args: impl Iterator<Item = DiagArg<'a>>,
1733    ) -> SubdiagMessage {
1734        SubdiagMessage::Translated(Cow::from(self.eagerly_translate_to_string(message, args)))
1735    }
1736
1737    /// Translate `message` eagerly with `args` to `String`.
1738    fn eagerly_translate_to_string<'a>(
1739        &self,
1740        message: DiagMessage,
1741        args: impl Iterator<Item = DiagArg<'a>>,
1742    ) -> String {
1743        let args = crate::translation::to_fluent_args(args);
1744        self.emitter
1745            .translator()
1746            .translate_message(&message, &args)
1747            .map_err(Report::new)
1748            .unwrap()
1749            .to_string()
1750    }
1751
1752    fn eagerly_translate_for_subdiag(
1753        &self,
1754        diag: &DiagInner,
1755        msg: impl Into<SubdiagMessage>,
1756    ) -> SubdiagMessage {
1757        let msg = diag.subdiagnostic_message_to_diagnostic_message(msg);
1758        self.eagerly_translate(msg, diag.args.iter())
1759    }
1760
1761    fn flush_delayed(&mut self) {
1762        // Stashed diagnostics must be emitted before delayed bugs are flushed.
1763        // Otherwise, we might ICE prematurely when errors would have
1764        // eventually happened.
1765        assert!(self.stashed_diagnostics.is_empty());
1766
1767        if !self.err_guars.is_empty() {
1768            // If an error happened already. We shouldn't expose delayed bugs.
1769            return;
1770        }
1771
1772        if self.delayed_bugs.is_empty() {
1773            // Nothing to do.
1774            return;
1775        }
1776
1777        let bugs: Vec<_> =
1778            std::mem::take(&mut self.delayed_bugs).into_iter().map(|(b, _)| b).collect();
1779
1780        let backtrace = std::env::var_os("RUST_BACKTRACE").as_deref() != Some(OsStr::new("0"));
1781        let decorate = backtrace || self.ice_file.is_none();
1782        let mut out = self
1783            .ice_file
1784            .as_ref()
1785            .and_then(|file| std::fs::File::options().create(true).append(true).open(file).ok());
1786
1787        // Put the overall explanation before the `DelayedBug`s, to frame them
1788        // better (e.g. separate warnings from them). Also, use notes, which
1789        // don't count as errors, to avoid possibly triggering
1790        // `-Ztreat-err-as-bug`, which we don't want.
1791        let note1 = "no errors encountered even though delayed bugs were created";
1792        let note2 = "those delayed bugs will now be shown as internal compiler errors";
1793        self.emit_diagnostic(DiagInner::new(Note, note1), None);
1794        self.emit_diagnostic(DiagInner::new(Note, note2), None);
1795
1796        for bug in bugs {
1797            if let Some(out) = &mut out {
1798                _ = write!(
1799                    out,
1800                    "delayed bug: {}\n{}\n",
1801                    bug.inner
1802                        .messages
1803                        .iter()
1804                        .filter_map(|(msg, _)| msg.as_str())
1805                        .collect::<String>(),
1806                    &bug.note
1807                );
1808            }
1809
1810            let mut bug = if decorate { bug.decorate(self) } else { bug.inner };
1811
1812            // "Undelay" the delayed bugs into plain bugs.
1813            if bug.level != DelayedBug {
1814                // NOTE(eddyb) not panicking here because we're already producing
1815                // an ICE, and the more information the merrier.
1816                //
1817                // We are at the `DiagInner`/`DiagCtxtInner` level rather than
1818                // the usual `Diag`/`DiagCtxt` level, so we must augment `bug`
1819                // in a lower-level fashion.
1820                bug.arg("level", bug.level);
1821                let msg = crate::fluent_generated::errors_invalid_flushed_delayed_diagnostic_level;
1822                let msg = self.eagerly_translate_for_subdiag(&bug, msg); // after the `arg` call
1823                bug.sub(Note, msg, bug.span.primary_span().unwrap().into());
1824            }
1825            bug.level = Bug;
1826
1827            self.emit_diagnostic(bug, None);
1828        }
1829
1830        // Panic with `DelayedBugPanic` to avoid "unexpected panic" messages.
1831        panic::panic_any(DelayedBugPanic);
1832    }
1833
1834    fn panic_if_treat_err_as_bug(&self) {
1835        if self.treat_err_as_bug() {
1836            let n = self.flags.treat_err_as_bug.map(|c| c.get()).unwrap();
1837            assert_eq!(n, self.err_guars.len() + self.lint_err_guars.len());
1838            if n == 1 {
1839                panic!("aborting due to `-Z treat-err-as-bug=1`");
1840            } else {
1841                panic!("aborting after {n} errors due to `-Z treat-err-as-bug={n}`");
1842            }
1843        }
1844    }
1845}
1846
1847struct DelayedDiagInner {
1848    inner: DiagInner,
1849    note: Backtrace,
1850}
1851
1852impl DelayedDiagInner {
1853    fn with_backtrace(diagnostic: DiagInner, backtrace: Backtrace) -> Self {
1854        DelayedDiagInner { inner: diagnostic, note: backtrace }
1855    }
1856
1857    fn decorate(self, dcx: &DiagCtxtInner) -> DiagInner {
1858        // We are at the `DiagInner`/`DiagCtxtInner` level rather than the
1859        // usual `Diag`/`DiagCtxt` level, so we must construct `diag` in a
1860        // lower-level fashion.
1861        let mut diag = self.inner;
1862        let msg = match self.note.status() {
1863            BacktraceStatus::Captured => crate::fluent_generated::errors_delayed_at_with_newline,
1864            // Avoid the needless newline when no backtrace has been captured,
1865            // the display impl should just be a single line.
1866            _ => crate::fluent_generated::errors_delayed_at_without_newline,
1867        };
1868        diag.arg("emitted_at", diag.emitted_at.clone());
1869        diag.arg("note", self.note);
1870        let msg = dcx.eagerly_translate_for_subdiag(&diag, msg); // after the `arg` calls
1871        diag.sub(Note, msg, diag.span.primary_span().unwrap_or(DUMMY_SP).into());
1872        diag
1873    }
1874}
1875
1876/// | Level        | is_error | EmissionGuarantee            | Top-level | Sub | Used in lints?
1877/// | -----        | -------- | -----------------            | --------- | --- | --------------
1878/// | Bug          | yes      | BugAbort                     | yes       | -   | -
1879/// | Fatal        | yes      | FatalAbort/FatalError[^star] | yes       | -   | -
1880/// | Error        | yes      | ErrorGuaranteed              | yes       | -   | yes
1881/// | DelayedBug   | yes      | ErrorGuaranteed              | yes       | -   | -
1882/// | ForceWarning | -        | ()                           | yes       | -   | lint-only
1883/// | Warning      | -        | ()                           | yes       | yes | yes
1884/// | Note         | -        | ()                           | rare      | yes | -
1885/// | OnceNote     | -        | ()                           | -         | yes | lint-only
1886/// | Help         | -        | ()                           | rare      | yes | -
1887/// | OnceHelp     | -        | ()                           | -         | yes | lint-only
1888/// | FailureNote  | -        | ()                           | rare      | -   | -
1889/// | Allow        | -        | ()                           | yes       | -   | lint-only
1890/// | Expect       | -        | ()                           | yes       | -   | lint-only
1891///
1892/// [^star]: `FatalAbort` normally, `FatalError` in the non-aborting "almost fatal" case that is
1893///     occasionally used.
1894///
1895#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug, Encodable, Decodable)]
1896pub enum Level {
1897    /// For bugs in the compiler. Manifests as an ICE (internal compiler error) panic.
1898    Bug,
1899
1900    /// An error that causes an immediate abort. Used for things like configuration errors,
1901    /// internal overflows, some file operation errors.
1902    Fatal,
1903
1904    /// An error in the code being compiled, which prevents compilation from finishing. This is the
1905    /// most common case.
1906    Error,
1907
1908    /// This is a strange one: lets you register an error without emitting it. If compilation ends
1909    /// without any other errors occurring, this will be emitted as a bug. Otherwise, it will be
1910    /// silently dropped. I.e. "expect other errors are emitted" semantics. Useful on code paths
1911    /// that should only be reached when compiling erroneous code.
1912    DelayedBug,
1913
1914    /// A `force-warn` lint warning about the code being compiled. Does not prevent compilation
1915    /// from finishing.
1916    ///
1917    /// Requires a [`LintExpectationId`] for expected lint diagnostics. In all other cases this
1918    /// should be `None`.
1919    ForceWarning,
1920
1921    /// A warning about the code being compiled. Does not prevent compilation from finishing.
1922    /// Will be skipped if `can_emit_warnings` is false.
1923    Warning,
1924
1925    /// A message giving additional context.
1926    Note,
1927
1928    /// A note that is only emitted once.
1929    OnceNote,
1930
1931    /// A message suggesting how to fix something.
1932    Help,
1933
1934    /// A help that is only emitted once.
1935    OnceHelp,
1936
1937    /// Similar to `Note`, but used in cases where compilation has failed. When printed for human
1938    /// consumption, it doesn't have any kind of `note:` label.
1939    FailureNote,
1940
1941    /// Only used for lints.
1942    Allow,
1943
1944    /// Only used for lints. Requires a [`LintExpectationId`] for silencing the lints.
1945    Expect,
1946}
1947
1948impl fmt::Display for Level {
1949    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1950        self.to_str().fmt(f)
1951    }
1952}
1953
1954impl Level {
1955    fn color(self) -> ColorSpec {
1956        let mut spec = ColorSpec::new();
1957        match self {
1958            Bug | Fatal | Error | DelayedBug => {
1959                spec.set_fg(Some(Color::Red)).set_intense(true);
1960            }
1961            ForceWarning | Warning => {
1962                spec.set_fg(Some(Color::Yellow)).set_intense(cfg!(windows));
1963            }
1964            Note | OnceNote => {
1965                spec.set_fg(Some(Color::Green)).set_intense(true);
1966            }
1967            Help | OnceHelp => {
1968                spec.set_fg(Some(Color::Cyan)).set_intense(true);
1969            }
1970            FailureNote => {}
1971            Allow | Expect => unreachable!(),
1972        }
1973        spec
1974    }
1975
1976    pub fn to_str(self) -> &'static str {
1977        match self {
1978            Bug | DelayedBug => "error: internal compiler error",
1979            Fatal | Error => "error",
1980            ForceWarning | Warning => "warning",
1981            Note | OnceNote => "note",
1982            Help | OnceHelp => "help",
1983            FailureNote => "failure-note",
1984            Allow | Expect => unreachable!(),
1985        }
1986    }
1987
1988    pub fn is_failure_note(&self) -> bool {
1989        matches!(*self, FailureNote)
1990    }
1991
1992    // Can this level be used in a subdiagnostic message?
1993    fn can_be_subdiag(&self) -> bool {
1994        match self {
1995            Bug | DelayedBug | Fatal | Error | ForceWarning | FailureNote | Allow | Expect => false,
1996
1997            Warning | Note | Help | OnceNote | OnceHelp => true,
1998        }
1999    }
2000}
2001
2002// FIXME(eddyb) this doesn't belong here AFAICT, should be moved to callsite.
2003pub fn elided_lifetime_in_path_suggestion(
2004    source_map: &SourceMap,
2005    n: usize,
2006    path_span: Span,
2007    incl_angl_brckt: bool,
2008    insertion_span: Span,
2009) -> ElidedLifetimeInPathSubdiag {
2010    let expected = ExpectedLifetimeParameter { span: path_span, count: n };
2011    // Do not try to suggest anything if generated by a proc-macro.
2012    let indicate = source_map.is_span_accessible(insertion_span).then(|| {
2013        let anon_lts = vec!["'_"; n].join(", ");
2014        let suggestion =
2015            if incl_angl_brckt { format!("<{anon_lts}>") } else { format!("{anon_lts}, ") };
2016
2017        IndicateAnonymousLifetime { span: insertion_span.shrink_to_hi(), count: n, suggestion }
2018    });
2019
2020    ElidedLifetimeInPathSubdiag { expected, indicate }
2021}
2022
2023pub fn report_ambiguity_error<'a, G: EmissionGuarantee>(
2024    diag: &mut Diag<'a, G>,
2025    ambiguity: rustc_lint_defs::AmbiguityErrorDiag,
2026) {
2027    diag.span_label(ambiguity.label_span, ambiguity.label_msg);
2028    diag.note(ambiguity.note_msg);
2029    diag.span_note(ambiguity.b1_span, ambiguity.b1_note_msg);
2030    for help_msg in ambiguity.b1_help_msgs {
2031        diag.help(help_msg);
2032    }
2033    diag.span_note(ambiguity.b2_span, ambiguity.b2_note_msg);
2034    for help_msg in ambiguity.b2_help_msgs {
2035        diag.help(help_msg);
2036    }
2037}
2038
2039/// Grammatical tool for displaying messages to end users in a nice form.
2040///
2041/// Returns "an" if the given string starts with a vowel, and "a" otherwise.
2042pub fn a_or_an(s: &str) -> &'static str {
2043    let mut chars = s.chars();
2044    let Some(mut first_alpha_char) = chars.next() else {
2045        return "a";
2046    };
2047    if first_alpha_char == '`' {
2048        let Some(next) = chars.next() else {
2049            return "a";
2050        };
2051        first_alpha_char = next;
2052    }
2053    if ["a", "e", "i", "o", "u", "&"].contains(&&first_alpha_char.to_lowercase().to_string()[..]) {
2054        "an"
2055    } else {
2056        "a"
2057    }
2058}
2059
2060#[derive(Clone, Copy, PartialEq, Hash, Debug)]
2061pub enum TerminalUrl {
2062    No,
2063    Yes,
2064    Auto,
2065}
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy