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