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, fatal_note: Option<String>, emit_fatal_diagnostic: bool) {
752        // An empty type that implements `Emitter` to temporarily swap in place of the real one,
753        // which will be used in constructing its replacement.
754        struct FalseEmitter;
755
756        impl Emitter for FalseEmitter {
757            fn emit_diagnostic(&mut self, _: DiagInner, _: &Registry) {
758                unimplemented!("false emitter must only used during `make_silent`")
759            }
760
761            fn source_map(&self) -> Option<&SourceMap> {
762                unimplemented!("false emitter must only used during `make_silent`")
763            }
764        }
765
766        impl translation::Translate for FalseEmitter {
767            fn fluent_bundle(&self) -> Option<&FluentBundle> {
768                unimplemented!("false emitter must only used during `make_silent`")
769            }
770
771            fn fallback_fluent_bundle(&self) -> &FluentBundle {
772                unimplemented!("false emitter must only used during `make_silent`")
773            }
774        }
775
776        let mut inner = self.inner.borrow_mut();
777        let mut prev_emitter = Box::new(FalseEmitter) as Box<dyn Emitter + DynSend>;
778        std::mem::swap(&mut inner.emitter, &mut prev_emitter);
779        let new_emitter = Box::new(emitter::SilentEmitter {
780            fatal_emitter: prev_emitter,
781            fatal_note,
782            emit_fatal_diagnostic,
783        });
784        inner.emitter = new_emitter;
785    }
786
787    pub fn set_emitter(&self, emitter: Box<dyn Emitter + DynSend>) {
788        self.inner.borrow_mut().emitter = emitter;
789    }
790
791    /// Translate `message` eagerly with `args` to `SubdiagMessage::Eager`.
792    pub fn eagerly_translate<'a>(
793        &self,
794        message: DiagMessage,
795        args: impl Iterator<Item = DiagArg<'a>>,
796    ) -> SubdiagMessage {
797        let inner = self.inner.borrow();
798        inner.eagerly_translate(message, args)
799    }
800
801    /// Translate `message` eagerly with `args` to `String`.
802    pub fn eagerly_translate_to_string<'a>(
803        &self,
804        message: DiagMessage,
805        args: impl Iterator<Item = DiagArg<'a>>,
806    ) -> String {
807        let inner = self.inner.borrow();
808        inner.eagerly_translate_to_string(message, args)
809    }
810
811    // This is here to not allow mutation of flags;
812    // as of this writing it's used in Session::consider_optimizing and
813    // in tests in rustc_interface.
814    pub fn can_emit_warnings(&self) -> bool {
815        self.inner.borrow_mut().flags.can_emit_warnings
816    }
817
818    /// Resets the diagnostic error count as well as the cached emitted diagnostics.
819    ///
820    /// NOTE: *do not* call this function from rustc. It is only meant to be called from external
821    /// tools that want to reuse a `Parser` cleaning the previously emitted diagnostics as well as
822    /// the overall count of emitted error diagnostics.
823    pub fn reset_err_count(&self) {
824        // Use destructuring so that if a field gets added to `DiagCtxtInner`, it's impossible to
825        // fail to update this method as well.
826        let mut inner = self.inner.borrow_mut();
827        let DiagCtxtInner {
828            flags: _,
829            registry: _,
830            err_guars,
831            lint_err_guars,
832            delayed_bugs,
833            deduplicated_err_count,
834            deduplicated_warn_count,
835            emitter: _,
836            must_produce_diag,
837            has_printed,
838            suppressed_expected_diag,
839            taught_diagnostics,
840            emitted_diagnostic_codes,
841            emitted_diagnostics,
842            stashed_diagnostics,
843            future_breakage_diagnostics,
844            fulfilled_expectations,
845            ice_file: _,
846        } = inner.deref_mut();
847
848        // For the `Vec`s and `HashMap`s, we overwrite with an empty container to free the
849        // underlying memory (which `clear` would not do).
850        *err_guars = Default::default();
851        *lint_err_guars = Default::default();
852        *delayed_bugs = Default::default();
853        *deduplicated_err_count = 0;
854        *deduplicated_warn_count = 0;
855        *must_produce_diag = None;
856        *has_printed = false;
857        *suppressed_expected_diag = false;
858        *taught_diagnostics = Default::default();
859        *emitted_diagnostic_codes = Default::default();
860        *emitted_diagnostics = Default::default();
861        *stashed_diagnostics = Default::default();
862        *future_breakage_diagnostics = Default::default();
863        *fulfilled_expectations = Default::default();
864    }
865
866    pub fn handle<'a>(&'a self) -> DiagCtxtHandle<'a> {
867        DiagCtxtHandle { dcx: self, tainted_with_errors: None }
868    }
869
870    /// Link this to a taintable context so that emitting errors will automatically set
871    /// the `Option<ErrorGuaranteed>` instead of having to do that manually at every error
872    /// emission site.
873    pub fn taintable_handle<'a>(
874        &'a self,
875        tainted_with_errors: &'a Cell<Option<ErrorGuaranteed>>,
876    ) -> DiagCtxtHandle<'a> {
877        DiagCtxtHandle { dcx: self, tainted_with_errors: Some(tainted_with_errors) }
878    }
879}
880
881impl<'a> DiagCtxtHandle<'a> {
882    /// Stashes a diagnostic for possible later improvement in a different,
883    /// later stage of the compiler. Possible actions depend on the diagnostic
884    /// level:
885    /// - Level::Bug, Level:Fatal: not allowed, will trigger a panic.
886    /// - Level::Error: immediately counted as an error that has occurred, because it
887    ///   is guaranteed to be emitted eventually. Can be later accessed with the
888    ///   provided `span` and `key` through
889    ///   [`DiagCtxtHandle::try_steal_modify_and_emit_err`] or
890    ///   [`DiagCtxtHandle::try_steal_replace_and_emit_err`]. These do not allow
891    ///   cancellation or downgrading of the error. Returns
892    ///   `Some(ErrorGuaranteed)`.
893    /// - Level::DelayedBug: this does happen occasionally with errors that are
894    ///   downgraded to delayed bugs. It is not stashed, but immediately
895    ///   emitted as a delayed bug. This is because stashing it would cause it
896    ///   to be counted by `err_count` which we don't want. It doesn't matter
897    ///   that we cannot steal and improve it later, because it's not a
898    ///   user-facing error. Returns `Some(ErrorGuaranteed)` as is normal for
899    ///   delayed bugs.
900    /// - Level::Warning and lower (i.e. !is_error()): can be accessed with the
901    ///   provided `span` and `key` through [`DiagCtxtHandle::steal_non_err()`]. This
902    ///   allows cancelling and downgrading of the diagnostic. Returns `None`.
903    pub fn stash_diagnostic(
904        &self,
905        span: Span,
906        key: StashKey,
907        diag: DiagInner,
908    ) -> Option<ErrorGuaranteed> {
909        let guar = match diag.level {
910            Bug | Fatal => {
911                self.span_bug(
912                    span,
913                    format!("invalid level in `stash_diagnostic`: {:?}", diag.level),
914                );
915            }
916            // We delay a bug here so that `-Ztreat-err-as-bug -Zeagerly-emit-delayed-bugs`
917            // can be used to create a backtrace at the stashing site instead of whenever the
918            // diagnostic context is dropped and thus delayed bugs are emitted.
919            Error => Some(self.span_delayed_bug(span, format!("stashing {key:?}"))),
920            DelayedBug => {
921                return self.inner.borrow_mut().emit_diagnostic(diag, self.tainted_with_errors);
922            }
923            ForceWarning | Warning | Note | OnceNote | Help | OnceHelp | FailureNote | Allow
924            | Expect => None,
925        };
926
927        // FIXME(Centril, #69537): Consider reintroducing panic on overwriting a stashed diagnostic
928        // if/when we have a more robust macro-friendly replacement for `(span, key)` as a key.
929        // See the PR for a discussion.
930        self.inner
931            .borrow_mut()
932            .stashed_diagnostics
933            .entry(key)
934            .or_default()
935            .insert(span.with_parent(None), (diag, guar));
936
937        guar
938    }
939
940    /// Steal a previously stashed non-error diagnostic with the given `Span`
941    /// and [`StashKey`] as the key. Panics if the found diagnostic is an
942    /// error.
943    pub fn steal_non_err(self, span: Span, key: StashKey) -> Option<Diag<'a, ()>> {
944        // FIXME(#120456) - is `swap_remove` correct?
945        let (diag, guar) = self.inner.borrow_mut().stashed_diagnostics.get_mut(&key).and_then(
946            |stashed_diagnostics| stashed_diagnostics.swap_remove(&span.with_parent(None)),
947        )?;
948        assert!(!diag.is_error());
949        assert!(guar.is_none());
950        Some(Diag::new_diagnostic(self, diag))
951    }
952
953    /// Steals a previously stashed error with the given `Span` and
954    /// [`StashKey`] as the key, modifies it, and emits it. Returns `None` if
955    /// no matching diagnostic is found. Panics if the found diagnostic's level
956    /// isn't `Level::Error`.
957    pub fn try_steal_modify_and_emit_err<F>(
958        self,
959        span: Span,
960        key: StashKey,
961        mut modify_err: F,
962    ) -> Option<ErrorGuaranteed>
963    where
964        F: FnMut(&mut Diag<'_>),
965    {
966        // FIXME(#120456) - is `swap_remove` correct?
967        let err = self.inner.borrow_mut().stashed_diagnostics.get_mut(&key).and_then(
968            |stashed_diagnostics| stashed_diagnostics.swap_remove(&span.with_parent(None)),
969        );
970        err.map(|(err, guar)| {
971            // The use of `::<ErrorGuaranteed>` is safe because level is `Level::Error`.
972            assert_eq!(err.level, Error);
973            assert!(guar.is_some());
974            let mut err = Diag::<ErrorGuaranteed>::new_diagnostic(self, err);
975            modify_err(&mut err);
976            assert_eq!(err.level, Error);
977            err.emit()
978        })
979    }
980
981    /// Steals a previously stashed error with the given `Span` and
982    /// [`StashKey`] as the key, cancels it if found, and emits `new_err`.
983    /// Panics if the found diagnostic's level isn't `Level::Error`.
984    pub fn try_steal_replace_and_emit_err(
985        self,
986        span: Span,
987        key: StashKey,
988        new_err: Diag<'_>,
989    ) -> ErrorGuaranteed {
990        // FIXME(#120456) - is `swap_remove` correct?
991        let old_err = self.inner.borrow_mut().stashed_diagnostics.get_mut(&key).and_then(
992            |stashed_diagnostics| stashed_diagnostics.swap_remove(&span.with_parent(None)),
993        );
994        match old_err {
995            Some((old_err, guar)) => {
996                assert_eq!(old_err.level, Error);
997                assert!(guar.is_some());
998                // Because `old_err` has already been counted, it can only be
999                // safely cancelled because the `new_err` supplants it.
1000                Diag::<ErrorGuaranteed>::new_diagnostic(self, old_err).cancel();
1001            }
1002            None => {}
1003        };
1004        new_err.emit()
1005    }
1006
1007    pub fn has_stashed_diagnostic(&self, span: Span, key: StashKey) -> bool {
1008        let inner = self.inner.borrow();
1009        if let Some(stashed_diagnostics) = inner.stashed_diagnostics.get(&key)
1010            && !stashed_diagnostics.is_empty()
1011        {
1012            stashed_diagnostics.contains_key(&span.with_parent(None))
1013        } else {
1014            false
1015        }
1016    }
1017
1018    /// Emit all stashed diagnostics.
1019    pub fn emit_stashed_diagnostics(&self) -> Option<ErrorGuaranteed> {
1020        self.inner.borrow_mut().emit_stashed_diagnostics()
1021    }
1022
1023    /// This excludes delayed bugs.
1024    #[inline]
1025    pub fn err_count(&self) -> usize {
1026        let inner = self.inner.borrow();
1027        inner.err_guars.len()
1028            + inner.lint_err_guars.len()
1029            + inner
1030                .stashed_diagnostics
1031                .values()
1032                .map(|a| a.values().filter(|(_, guar)| guar.is_some()).count())
1033                .sum::<usize>()
1034    }
1035
1036    /// This excludes lint errors and delayed bugs. Unless absolutely
1037    /// necessary, prefer `has_errors` to this method.
1038    pub fn has_errors_excluding_lint_errors(&self) -> Option<ErrorGuaranteed> {
1039        self.inner.borrow().has_errors_excluding_lint_errors()
1040    }
1041
1042    /// This excludes delayed bugs.
1043    pub fn has_errors(&self) -> Option<ErrorGuaranteed> {
1044        self.inner.borrow().has_errors()
1045    }
1046
1047    /// This excludes nothing. Unless absolutely necessary, prefer `has_errors`
1048    /// to this method.
1049    pub fn has_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> {
1050        self.inner.borrow().has_errors_or_delayed_bugs()
1051    }
1052
1053    pub fn print_error_count(&self) {
1054        let mut inner = self.inner.borrow_mut();
1055
1056        // Any stashed diagnostics should have been handled by
1057        // `emit_stashed_diagnostics` by now.
1058        assert!(inner.stashed_diagnostics.is_empty());
1059
1060        if inner.treat_err_as_bug() {
1061            return;
1062        }
1063
1064        let warnings = match inner.deduplicated_warn_count {
1065            0 => Cow::from(""),
1066            1 => Cow::from("1 warning emitted"),
1067            count => Cow::from(format!("{count} warnings emitted")),
1068        };
1069        let errors = match inner.deduplicated_err_count {
1070            0 => Cow::from(""),
1071            1 => Cow::from("aborting due to 1 previous error"),
1072            count => Cow::from(format!("aborting due to {count} previous errors")),
1073        };
1074
1075        match (errors.len(), warnings.len()) {
1076            (0, 0) => return,
1077            (0, _) => {
1078                // Use `ForceWarning` rather than `Warning` to guarantee emission, e.g. with a
1079                // configuration like `--cap-lints allow --force-warn bare_trait_objects`.
1080                inner.emit_diagnostic(
1081                    DiagInner::new(ForceWarning, DiagMessage::Str(warnings)),
1082                    None,
1083                );
1084            }
1085            (_, 0) => {
1086                inner.emit_diagnostic(DiagInner::new(Error, errors), self.tainted_with_errors);
1087            }
1088            (_, _) => {
1089                inner.emit_diagnostic(
1090                    DiagInner::new(Error, format!("{errors}; {warnings}")),
1091                    self.tainted_with_errors,
1092                );
1093            }
1094        }
1095
1096        let can_show_explain = inner.emitter.should_show_explain();
1097        let are_there_diagnostics = !inner.emitted_diagnostic_codes.is_empty();
1098        if can_show_explain && are_there_diagnostics {
1099            let mut error_codes = inner
1100                .emitted_diagnostic_codes
1101                .iter()
1102                .filter_map(|&code| {
1103                    if inner.registry.try_find_description(code).is_ok() {
1104                        Some(code.to_string())
1105                    } else {
1106                        None
1107                    }
1108                })
1109                .collect::<Vec<_>>();
1110            if !error_codes.is_empty() {
1111                error_codes.sort();
1112                if error_codes.len() > 1 {
1113                    let limit = if error_codes.len() > 9 { 9 } else { error_codes.len() };
1114                    let msg1 = format!(
1115                        "Some errors have detailed explanations: {}{}",
1116                        error_codes[..limit].join(", "),
1117                        if error_codes.len() > 9 { "..." } else { "." }
1118                    );
1119                    let msg2 = format!(
1120                        "For more information about an error, try `rustc --explain {}`.",
1121                        &error_codes[0]
1122                    );
1123                    inner.emit_diagnostic(DiagInner::new(FailureNote, msg1), None);
1124                    inner.emit_diagnostic(DiagInner::new(FailureNote, msg2), None);
1125                } else {
1126                    let msg = format!(
1127                        "For more information about this error, try `rustc --explain {}`.",
1128                        &error_codes[0]
1129                    );
1130                    inner.emit_diagnostic(DiagInner::new(FailureNote, msg), None);
1131                }
1132            }
1133        }
1134    }
1135
1136    /// This excludes delayed bugs. Used for early aborts after errors occurred
1137    /// -- e.g. because continuing in the face of errors is likely to lead to
1138    /// bad results, such as spurious/uninteresting additional errors -- when
1139    /// returning an error `Result` is difficult.
1140    pub fn abort_if_errors(&self) {
1141        if let Some(guar) = self.has_errors() {
1142            guar.raise_fatal();
1143        }
1144    }
1145
1146    /// `true` if we haven't taught a diagnostic with this code already.
1147    /// The caller must then teach the user about such a diagnostic.
1148    ///
1149    /// Used to suppress emitting the same error multiple times with extended explanation when
1150    /// calling `-Zteach`.
1151    pub fn must_teach(&self, code: ErrCode) -> bool {
1152        self.inner.borrow_mut().taught_diagnostics.insert(code)
1153    }
1154
1155    pub fn emit_diagnostic(&self, diagnostic: DiagInner) -> Option<ErrorGuaranteed> {
1156        self.inner.borrow_mut().emit_diagnostic(diagnostic, self.tainted_with_errors)
1157    }
1158
1159    pub fn emit_artifact_notification(&self, path: &Path, artifact_type: &str) {
1160        self.inner.borrow_mut().emitter.emit_artifact_notification(path, artifact_type);
1161    }
1162
1163    pub fn emit_timing_section_start(&self, record: TimingRecord) {
1164        self.inner.borrow_mut().emitter.emit_timing_section(record, TimingEvent::Start);
1165    }
1166
1167    pub fn emit_timing_section_end(&self, record: TimingRecord) {
1168        self.inner.borrow_mut().emitter.emit_timing_section(record, TimingEvent::End);
1169    }
1170
1171    pub fn emit_future_breakage_report(&self) {
1172        let inner = &mut *self.inner.borrow_mut();
1173        let diags = std::mem::take(&mut inner.future_breakage_diagnostics);
1174        if !diags.is_empty() {
1175            inner.emitter.emit_future_breakage_report(diags, &inner.registry);
1176        }
1177    }
1178
1179    pub fn emit_unused_externs(
1180        &self,
1181        lint_level: rustc_lint_defs::Level,
1182        loud: bool,
1183        unused_externs: &[&str],
1184    ) {
1185        let mut inner = self.inner.borrow_mut();
1186
1187        // This "error" is an odd duck.
1188        // - It's only produce with JSON output.
1189        // - It's not emitted the usual way, via `emit_diagnostic`.
1190        // - The `$message_type` field is "unused_externs" rather than the usual
1191        //   "diagnosic".
1192        //
1193        // We count it as a lint error because it has a lint level. The value
1194        // of `loud` (which comes from "unused-externs" or
1195        // "unused-externs-silent"), also affects whether it's treated like a
1196        // hard error or not.
1197        if loud && lint_level.is_error() {
1198            // This `unchecked_error_guaranteed` is valid. It is where the
1199            // `ErrorGuaranteed` for unused_extern errors originates.
1200            #[allow(deprecated)]
1201            inner.lint_err_guars.push(ErrorGuaranteed::unchecked_error_guaranteed());
1202            inner.panic_if_treat_err_as_bug();
1203        }
1204
1205        inner.emitter.emit_unused_externs(lint_level, unused_externs)
1206    }
1207
1208    /// This methods steals all [`LintExpectationId`]s that are stored inside
1209    /// [`DiagCtxtInner`] and indicate that the linked expectation has been fulfilled.
1210    #[must_use]
1211    pub fn steal_fulfilled_expectation_ids(&self) -> FxIndexSet<LintExpectationId> {
1212        std::mem::take(&mut self.inner.borrow_mut().fulfilled_expectations)
1213    }
1214
1215    pub fn flush_delayed(&self) {
1216        self.inner.borrow_mut().flush_delayed();
1217    }
1218
1219    /// Used when trimmed_def_paths is called and we must produce a diagnostic
1220    /// to justify its cost.
1221    #[track_caller]
1222    pub fn set_must_produce_diag(&self) {
1223        assert!(
1224            self.inner.borrow().must_produce_diag.is_none(),
1225            "should only need to collect a backtrace once"
1226        );
1227        self.inner.borrow_mut().must_produce_diag = Some(Backtrace::capture());
1228    }
1229}
1230
1231// This `impl` block contains only the public diagnostic creation/emission API.
1232//
1233// Functions beginning with `struct_`/`create_` create a diagnostic. Other
1234// functions create and emit a diagnostic all in one go.
1235impl<'a> DiagCtxtHandle<'a> {
1236    // No `#[rustc_lint_diagnostics]` and no `impl Into<DiagMessage>` because bug messages aren't
1237    // user-facing.
1238    #[track_caller]
1239    pub fn struct_bug(self, msg: impl Into<Cow<'static, str>>) -> Diag<'a, BugAbort> {
1240        Diag::new(self, Bug, msg.into())
1241    }
1242
1243    // No `#[rustc_lint_diagnostics]` and no `impl Into<DiagMessage>` because bug messages aren't
1244    // user-facing.
1245    #[track_caller]
1246    pub fn bug(self, msg: impl Into<Cow<'static, str>>) -> ! {
1247        self.struct_bug(msg).emit()
1248    }
1249
1250    // No `#[rustc_lint_diagnostics]` and no `impl Into<DiagMessage>` because bug messages aren't
1251    // user-facing.
1252    #[track_caller]
1253    pub fn struct_span_bug(
1254        self,
1255        span: impl Into<MultiSpan>,
1256        msg: impl Into<Cow<'static, str>>,
1257    ) -> Diag<'a, BugAbort> {
1258        self.struct_bug(msg).with_span(span)
1259    }
1260
1261    // No `#[rustc_lint_diagnostics]` and no `impl Into<DiagMessage>` because bug messages aren't
1262    // user-facing.
1263    #[track_caller]
1264    pub fn span_bug(self, span: impl Into<MultiSpan>, msg: impl Into<Cow<'static, str>>) -> ! {
1265        self.struct_span_bug(span, msg.into()).emit()
1266    }
1267
1268    #[track_caller]
1269    pub fn create_bug(self, bug: impl Diagnostic<'a, BugAbort>) -> Diag<'a, BugAbort> {
1270        bug.into_diag(self, Bug)
1271    }
1272
1273    #[track_caller]
1274    pub fn emit_bug(self, bug: impl Diagnostic<'a, BugAbort>) -> ! {
1275        self.create_bug(bug).emit()
1276    }
1277
1278    #[rustc_lint_diagnostics]
1279    #[track_caller]
1280    pub fn struct_fatal(self, msg: impl Into<DiagMessage>) -> Diag<'a, FatalAbort> {
1281        Diag::new(self, Fatal, msg)
1282    }
1283
1284    #[rustc_lint_diagnostics]
1285    #[track_caller]
1286    pub fn fatal(self, msg: impl Into<DiagMessage>) -> ! {
1287        self.struct_fatal(msg).emit()
1288    }
1289
1290    #[rustc_lint_diagnostics]
1291    #[track_caller]
1292    pub fn struct_span_fatal(
1293        self,
1294        span: impl Into<MultiSpan>,
1295        msg: impl Into<DiagMessage>,
1296    ) -> Diag<'a, FatalAbort> {
1297        self.struct_fatal(msg).with_span(span)
1298    }
1299
1300    #[rustc_lint_diagnostics]
1301    #[track_caller]
1302    pub fn span_fatal(self, span: impl Into<MultiSpan>, msg: impl Into<DiagMessage>) -> ! {
1303        self.struct_span_fatal(span, msg).emit()
1304    }
1305
1306    #[track_caller]
1307    pub fn create_fatal(self, fatal: impl Diagnostic<'a, FatalAbort>) -> Diag<'a, FatalAbort> {
1308        fatal.into_diag(self, Fatal)
1309    }
1310
1311    #[track_caller]
1312    pub fn emit_fatal(self, fatal: impl Diagnostic<'a, FatalAbort>) -> ! {
1313        self.create_fatal(fatal).emit()
1314    }
1315
1316    #[track_caller]
1317    pub fn create_almost_fatal(
1318        self,
1319        fatal: impl Diagnostic<'a, FatalError>,
1320    ) -> Diag<'a, FatalError> {
1321        fatal.into_diag(self, Fatal)
1322    }
1323
1324    #[track_caller]
1325    pub fn emit_almost_fatal(self, fatal: impl Diagnostic<'a, FatalError>) -> FatalError {
1326        self.create_almost_fatal(fatal).emit()
1327    }
1328
1329    // FIXME: This method should be removed (every error should have an associated error code).
1330    #[rustc_lint_diagnostics]
1331    #[track_caller]
1332    pub fn struct_err(self, msg: impl Into<DiagMessage>) -> Diag<'a> {
1333        Diag::new(self, Error, msg)
1334    }
1335
1336    #[rustc_lint_diagnostics]
1337    #[track_caller]
1338    pub fn err(self, msg: impl Into<DiagMessage>) -> ErrorGuaranteed {
1339        self.struct_err(msg).emit()
1340    }
1341
1342    #[rustc_lint_diagnostics]
1343    #[track_caller]
1344    pub fn struct_span_err(
1345        self,
1346        span: impl Into<MultiSpan>,
1347        msg: impl Into<DiagMessage>,
1348    ) -> Diag<'a> {
1349        self.struct_err(msg).with_span(span)
1350    }
1351
1352    #[rustc_lint_diagnostics]
1353    #[track_caller]
1354    pub fn span_err(
1355        self,
1356        span: impl Into<MultiSpan>,
1357        msg: impl Into<DiagMessage>,
1358    ) -> ErrorGuaranteed {
1359        self.struct_span_err(span, msg).emit()
1360    }
1361
1362    #[track_caller]
1363    pub fn create_err(self, err: impl Diagnostic<'a>) -> Diag<'a> {
1364        err.into_diag(self, Error)
1365    }
1366
1367    #[track_caller]
1368    pub fn emit_err(self, err: impl Diagnostic<'a>) -> ErrorGuaranteed {
1369        self.create_err(err).emit()
1370    }
1371
1372    /// Ensures that an error is printed. See `Level::DelayedBug`.
1373    //
1374    // No `#[rustc_lint_diagnostics]` and no `impl Into<DiagMessage>` because bug messages aren't
1375    // user-facing.
1376    #[track_caller]
1377    pub fn delayed_bug(self, msg: impl Into<Cow<'static, str>>) -> ErrorGuaranteed {
1378        Diag::<ErrorGuaranteed>::new(self, DelayedBug, msg.into()).emit()
1379    }
1380
1381    /// Ensures that an error is printed. See [`Level::DelayedBug`].
1382    ///
1383    /// Note: this function used to be called `delay_span_bug`. It was renamed
1384    /// to match similar functions like `span_err`, `span_warn`, etc.
1385    //
1386    // No `#[rustc_lint_diagnostics]` and no `impl Into<DiagMessage>` because bug messages aren't
1387    // user-facing.
1388    #[track_caller]
1389    pub fn span_delayed_bug(
1390        self,
1391        sp: impl Into<MultiSpan>,
1392        msg: impl Into<Cow<'static, str>>,
1393    ) -> ErrorGuaranteed {
1394        Diag::<ErrorGuaranteed>::new(self, DelayedBug, msg.into()).with_span(sp).emit()
1395    }
1396
1397    #[rustc_lint_diagnostics]
1398    #[track_caller]
1399    pub fn struct_warn(self, msg: impl Into<DiagMessage>) -> Diag<'a, ()> {
1400        Diag::new(self, Warning, msg)
1401    }
1402
1403    #[rustc_lint_diagnostics]
1404    #[track_caller]
1405    pub fn warn(self, msg: impl Into<DiagMessage>) {
1406        self.struct_warn(msg).emit()
1407    }
1408
1409    #[rustc_lint_diagnostics]
1410    #[track_caller]
1411    pub fn struct_span_warn(
1412        self,
1413        span: impl Into<MultiSpan>,
1414        msg: impl Into<DiagMessage>,
1415    ) -> Diag<'a, ()> {
1416        self.struct_warn(msg).with_span(span)
1417    }
1418
1419    #[rustc_lint_diagnostics]
1420    #[track_caller]
1421    pub fn span_warn(self, span: impl Into<MultiSpan>, msg: impl Into<DiagMessage>) {
1422        self.struct_span_warn(span, msg).emit()
1423    }
1424
1425    #[track_caller]
1426    pub fn create_warn(self, warning: impl Diagnostic<'a, ()>) -> Diag<'a, ()> {
1427        warning.into_diag(self, Warning)
1428    }
1429
1430    #[track_caller]
1431    pub fn emit_warn(self, warning: impl Diagnostic<'a, ()>) {
1432        self.create_warn(warning).emit()
1433    }
1434
1435    #[rustc_lint_diagnostics]
1436    #[track_caller]
1437    pub fn struct_note(self, msg: impl Into<DiagMessage>) -> Diag<'a, ()> {
1438        Diag::new(self, Note, msg)
1439    }
1440
1441    #[rustc_lint_diagnostics]
1442    #[track_caller]
1443    pub fn note(&self, msg: impl Into<DiagMessage>) {
1444        self.struct_note(msg).emit()
1445    }
1446
1447    #[rustc_lint_diagnostics]
1448    #[track_caller]
1449    pub fn struct_span_note(
1450        self,
1451        span: impl Into<MultiSpan>,
1452        msg: impl Into<DiagMessage>,
1453    ) -> Diag<'a, ()> {
1454        self.struct_note(msg).with_span(span)
1455    }
1456
1457    #[rustc_lint_diagnostics]
1458    #[track_caller]
1459    pub fn span_note(self, span: impl Into<MultiSpan>, msg: impl Into<DiagMessage>) {
1460        self.struct_span_note(span, msg).emit()
1461    }
1462
1463    #[track_caller]
1464    pub fn create_note(self, note: impl Diagnostic<'a, ()>) -> Diag<'a, ()> {
1465        note.into_diag(self, Note)
1466    }
1467
1468    #[track_caller]
1469    pub fn emit_note(self, note: impl Diagnostic<'a, ()>) {
1470        self.create_note(note).emit()
1471    }
1472
1473    #[rustc_lint_diagnostics]
1474    #[track_caller]
1475    pub fn struct_help(self, msg: impl Into<DiagMessage>) -> Diag<'a, ()> {
1476        Diag::new(self, Help, msg)
1477    }
1478
1479    #[rustc_lint_diagnostics]
1480    #[track_caller]
1481    pub fn struct_failure_note(self, msg: impl Into<DiagMessage>) -> Diag<'a, ()> {
1482        Diag::new(self, FailureNote, msg)
1483    }
1484
1485    #[rustc_lint_diagnostics]
1486    #[track_caller]
1487    pub fn struct_allow(self, msg: impl Into<DiagMessage>) -> Diag<'a, ()> {
1488        Diag::new(self, Allow, msg)
1489    }
1490
1491    #[rustc_lint_diagnostics]
1492    #[track_caller]
1493    pub fn struct_expect(self, msg: impl Into<DiagMessage>, id: LintExpectationId) -> Diag<'a, ()> {
1494        Diag::new(self, Expect, msg).with_lint_id(id)
1495    }
1496}
1497
1498// Note: we prefer implementing operations on `DiagCtxt`, rather than
1499// `DiagCtxtInner`, whenever possible. This minimizes functions where
1500// `DiagCtxt::foo()` just borrows `inner` and forwards a call to
1501// `DiagCtxtInner::foo`.
1502impl DiagCtxtInner {
1503    fn new(emitter: Box<DynEmitter>) -> Self {
1504        Self {
1505            flags: DiagCtxtFlags { can_emit_warnings: true, ..Default::default() },
1506            registry: Registry::new(&[]),
1507            err_guars: Vec::new(),
1508            lint_err_guars: Vec::new(),
1509            delayed_bugs: Vec::new(),
1510            deduplicated_err_count: 0,
1511            deduplicated_warn_count: 0,
1512            emitter,
1513            must_produce_diag: None,
1514            has_printed: false,
1515            suppressed_expected_diag: false,
1516            taught_diagnostics: Default::default(),
1517            emitted_diagnostic_codes: Default::default(),
1518            emitted_diagnostics: Default::default(),
1519            stashed_diagnostics: Default::default(),
1520            future_breakage_diagnostics: Vec::new(),
1521            fulfilled_expectations: Default::default(),
1522            ice_file: None,
1523        }
1524    }
1525
1526    /// Emit all stashed diagnostics.
1527    fn emit_stashed_diagnostics(&mut self) -> Option<ErrorGuaranteed> {
1528        let mut guar = None;
1529        let has_errors = !self.err_guars.is_empty();
1530        for (_, stashed_diagnostics) in std::mem::take(&mut self.stashed_diagnostics).into_iter() {
1531            for (_, (diag, _guar)) in stashed_diagnostics {
1532                if !diag.is_error() {
1533                    // Unless they're forced, don't flush stashed warnings when
1534                    // there are errors, to avoid causing warning overload. The
1535                    // stash would've been stolen already if it were important.
1536                    if !diag.is_force_warn() && has_errors {
1537                        continue;
1538                    }
1539                }
1540                guar = guar.or(self.emit_diagnostic(diag, None));
1541            }
1542        }
1543        guar
1544    }
1545
1546    // Return value is only `Some` if the level is `Error` or `DelayedBug`.
1547    fn emit_diagnostic(
1548        &mut self,
1549        mut diagnostic: DiagInner,
1550        taint: Option<&Cell<Option<ErrorGuaranteed>>>,
1551    ) -> Option<ErrorGuaranteed> {
1552        if diagnostic.has_future_breakage() {
1553            // Future breakages aren't emitted if they're `Level::Allow` or
1554            // `Level::Expect`, but they still need to be constructed and
1555            // stashed below, so they'll trigger the must_produce_diag check.
1556            assert_matches!(diagnostic.level, Error | ForceWarning | Warning | Allow | Expect);
1557            self.future_breakage_diagnostics.push(diagnostic.clone());
1558        }
1559
1560        // We call TRACK_DIAGNOSTIC with an empty closure for the cases that
1561        // return early *and* have some kind of side-effect, except where
1562        // noted.
1563        match diagnostic.level {
1564            Bug => {}
1565            Fatal | Error => {
1566                if self.treat_next_err_as_bug() {
1567                    // `Fatal` and `Error` can be promoted to `Bug`.
1568                    diagnostic.level = Bug;
1569                }
1570            }
1571            DelayedBug => {
1572                // Note that because we check these conditions first,
1573                // `-Zeagerly-emit-delayed-bugs` and `-Ztreat-err-as-bug`
1574                // continue to work even after we've issued an error and
1575                // stopped recording new delayed bugs.
1576                if self.flags.eagerly_emit_delayed_bugs {
1577                    // `DelayedBug` can be promoted to `Error` or `Bug`.
1578                    if self.treat_next_err_as_bug() {
1579                        diagnostic.level = Bug;
1580                    } else {
1581                        diagnostic.level = Error;
1582                    }
1583                } else {
1584                    // If we have already emitted at least one error, we don't need
1585                    // to record the delayed bug, because it'll never be used.
1586                    return if let Some(guar) = self.has_errors() {
1587                        Some(guar)
1588                    } else {
1589                        // No `TRACK_DIAGNOSTIC` call is needed, because the
1590                        // incremental session is deleted if there is a delayed
1591                        // bug. This also saves us from cloning the diagnostic.
1592                        let backtrace = std::backtrace::Backtrace::capture();
1593                        // This `unchecked_error_guaranteed` is valid. It is where the
1594                        // `ErrorGuaranteed` for delayed bugs originates. See
1595                        // `DiagCtxtInner::drop`.
1596                        #[allow(deprecated)]
1597                        let guar = ErrorGuaranteed::unchecked_error_guaranteed();
1598                        self.delayed_bugs
1599                            .push((DelayedDiagInner::with_backtrace(diagnostic, backtrace), guar));
1600                        Some(guar)
1601                    };
1602                }
1603            }
1604            ForceWarning if diagnostic.lint_id.is_none() => {} // `ForceWarning(Some(...))` is below, with `Expect`
1605            Warning => {
1606                if !self.flags.can_emit_warnings {
1607                    // We are not emitting warnings.
1608                    if diagnostic.has_future_breakage() {
1609                        // The side-effect is at the top of this method.
1610                        TRACK_DIAGNOSTIC(diagnostic, &mut |_| None);
1611                    }
1612                    return None;
1613                }
1614            }
1615            Note | Help | FailureNote => {}
1616            OnceNote | OnceHelp => panic!("bad level: {:?}", diagnostic.level),
1617            Allow => {
1618                // Nothing emitted for allowed lints.
1619                if diagnostic.has_future_breakage() {
1620                    // The side-effect is at the top of this method.
1621                    TRACK_DIAGNOSTIC(diagnostic, &mut |_| None);
1622                    self.suppressed_expected_diag = true;
1623                }
1624                return None;
1625            }
1626            Expect | ForceWarning => {
1627                self.fulfilled_expectations.insert(diagnostic.lint_id.unwrap());
1628                if let Expect = diagnostic.level {
1629                    // Nothing emitted here for expected lints.
1630                    TRACK_DIAGNOSTIC(diagnostic, &mut |_| None);
1631                    self.suppressed_expected_diag = true;
1632                    return None;
1633                }
1634            }
1635        }
1636
1637        TRACK_DIAGNOSTIC(diagnostic, &mut |mut diagnostic| {
1638            if let Some(code) = diagnostic.code {
1639                self.emitted_diagnostic_codes.insert(code);
1640            }
1641
1642            let already_emitted = {
1643                let mut hasher = StableHasher::new();
1644                diagnostic.hash(&mut hasher);
1645                let diagnostic_hash = hasher.finish();
1646                !self.emitted_diagnostics.insert(diagnostic_hash)
1647            };
1648
1649            let is_error = diagnostic.is_error();
1650            let is_lint = diagnostic.is_lint.is_some();
1651
1652            // Only emit the diagnostic if we've been asked to deduplicate or
1653            // haven't already emitted an equivalent diagnostic.
1654            if !(self.flags.deduplicate_diagnostics && already_emitted) {
1655                debug!(?diagnostic);
1656                debug!(?self.emitted_diagnostics);
1657
1658                let not_yet_emitted = |sub: &mut Subdiag| {
1659                    debug!(?sub);
1660                    if sub.level != OnceNote && sub.level != OnceHelp {
1661                        return true;
1662                    }
1663                    let mut hasher = StableHasher::new();
1664                    sub.hash(&mut hasher);
1665                    let diagnostic_hash = hasher.finish();
1666                    debug!(?diagnostic_hash);
1667                    self.emitted_diagnostics.insert(diagnostic_hash)
1668                };
1669                diagnostic.children.retain_mut(not_yet_emitted);
1670                if already_emitted {
1671                    let msg = "duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`";
1672                    diagnostic.sub(Note, msg, MultiSpan::new());
1673                }
1674
1675                if is_error {
1676                    self.deduplicated_err_count += 1;
1677                } else if matches!(diagnostic.level, ForceWarning | Warning) {
1678                    self.deduplicated_warn_count += 1;
1679                }
1680                self.has_printed = true;
1681
1682                self.emitter.emit_diagnostic(diagnostic, &self.registry);
1683            }
1684
1685            if is_error {
1686                // If we have any delayed bugs recorded, we can discard them
1687                // because they won't be used. (This should only occur if there
1688                // have been no errors previously emitted, because we don't add
1689                // new delayed bugs once the first error is emitted.)
1690                if !self.delayed_bugs.is_empty() {
1691                    assert_eq!(self.lint_err_guars.len() + self.err_guars.len(), 0);
1692                    self.delayed_bugs.clear();
1693                    self.delayed_bugs.shrink_to_fit();
1694                }
1695
1696                // This `unchecked_error_guaranteed` is valid. It is where the
1697                // `ErrorGuaranteed` for errors and lint errors originates.
1698                #[allow(deprecated)]
1699                let guar = ErrorGuaranteed::unchecked_error_guaranteed();
1700                if is_lint {
1701                    self.lint_err_guars.push(guar);
1702                } else {
1703                    if let Some(taint) = taint {
1704                        taint.set(Some(guar));
1705                    }
1706                    self.err_guars.push(guar);
1707                }
1708                self.panic_if_treat_err_as_bug();
1709                Some(guar)
1710            } else {
1711                None
1712            }
1713        })
1714    }
1715
1716    fn treat_err_as_bug(&self) -> bool {
1717        self.flags
1718            .treat_err_as_bug
1719            .is_some_and(|c| self.err_guars.len() + self.lint_err_guars.len() >= c.get())
1720    }
1721
1722    // Use this one before incrementing `err_count`.
1723    fn treat_next_err_as_bug(&self) -> bool {
1724        self.flags
1725            .treat_err_as_bug
1726            .is_some_and(|c| self.err_guars.len() + self.lint_err_guars.len() + 1 >= c.get())
1727    }
1728
1729    fn has_errors_excluding_lint_errors(&self) -> Option<ErrorGuaranteed> {
1730        self.err_guars.get(0).copied().or_else(|| {
1731            if let Some((_diag, guar)) = self
1732                .stashed_diagnostics
1733                .values()
1734                .flat_map(|stashed_diagnostics| stashed_diagnostics.values())
1735                .find(|(diag, guar)| guar.is_some() && diag.is_lint.is_none())
1736            {
1737                *guar
1738            } else {
1739                None
1740            }
1741        })
1742    }
1743
1744    fn has_errors(&self) -> Option<ErrorGuaranteed> {
1745        self.err_guars.get(0).copied().or_else(|| self.lint_err_guars.get(0).copied()).or_else(
1746            || {
1747                self.stashed_diagnostics.values().find_map(|stashed_diagnostics| {
1748                    stashed_diagnostics.values().find_map(|(_, guar)| *guar)
1749                })
1750            },
1751        )
1752    }
1753
1754    fn has_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> {
1755        self.has_errors().or_else(|| self.delayed_bugs.get(0).map(|(_, guar)| guar).copied())
1756    }
1757
1758    /// Translate `message` eagerly with `args` to `SubdiagMessage::Eager`.
1759    fn eagerly_translate<'a>(
1760        &self,
1761        message: DiagMessage,
1762        args: impl Iterator<Item = DiagArg<'a>>,
1763    ) -> SubdiagMessage {
1764        SubdiagMessage::Translated(Cow::from(self.eagerly_translate_to_string(message, args)))
1765    }
1766
1767    /// Translate `message` eagerly with `args` to `String`.
1768    fn eagerly_translate_to_string<'a>(
1769        &self,
1770        message: DiagMessage,
1771        args: impl Iterator<Item = DiagArg<'a>>,
1772    ) -> String {
1773        let args = crate::translation::to_fluent_args(args);
1774        self.emitter.translate_message(&message, &args).map_err(Report::new).unwrap().to_string()
1775    }
1776
1777    fn eagerly_translate_for_subdiag(
1778        &self,
1779        diag: &DiagInner,
1780        msg: impl Into<SubdiagMessage>,
1781    ) -> SubdiagMessage {
1782        let msg = diag.subdiagnostic_message_to_diagnostic_message(msg);
1783        self.eagerly_translate(msg, diag.args.iter())
1784    }
1785
1786    fn flush_delayed(&mut self) {
1787        // Stashed diagnostics must be emitted before delayed bugs are flushed.
1788        // Otherwise, we might ICE prematurely when errors would have
1789        // eventually happened.
1790        assert!(self.stashed_diagnostics.is_empty());
1791
1792        if !self.err_guars.is_empty() {
1793            // If an error happened already. We shouldn't expose delayed bugs.
1794            return;
1795        }
1796
1797        if self.delayed_bugs.is_empty() {
1798            // Nothing to do.
1799            return;
1800        }
1801
1802        let bugs: Vec<_> =
1803            std::mem::take(&mut self.delayed_bugs).into_iter().map(|(b, _)| b).collect();
1804
1805        let backtrace = std::env::var_os("RUST_BACKTRACE").as_deref() != Some(OsStr::new("0"));
1806        let decorate = backtrace || self.ice_file.is_none();
1807        let mut out = self
1808            .ice_file
1809            .as_ref()
1810            .and_then(|file| std::fs::File::options().create(true).append(true).open(file).ok());
1811
1812        // Put the overall explanation before the `DelayedBug`s, to frame them
1813        // better (e.g. separate warnings from them). Also, use notes, which
1814        // don't count as errors, to avoid possibly triggering
1815        // `-Ztreat-err-as-bug`, which we don't want.
1816        let note1 = "no errors encountered even though delayed bugs were created";
1817        let note2 = "those delayed bugs will now be shown as internal compiler errors";
1818        self.emit_diagnostic(DiagInner::new(Note, note1), None);
1819        self.emit_diagnostic(DiagInner::new(Note, note2), None);
1820
1821        for bug in bugs {
1822            if let Some(out) = &mut out {
1823                _ = write!(
1824                    out,
1825                    "delayed bug: {}\n{}\n",
1826                    bug.inner
1827                        .messages
1828                        .iter()
1829                        .filter_map(|(msg, _)| msg.as_str())
1830                        .collect::<String>(),
1831                    &bug.note
1832                );
1833            }
1834
1835            let mut bug = if decorate { bug.decorate(self) } else { bug.inner };
1836
1837            // "Undelay" the delayed bugs into plain bugs.
1838            if bug.level != DelayedBug {
1839                // NOTE(eddyb) not panicking here because we're already producing
1840                // an ICE, and the more information the merrier.
1841                //
1842                // We are at the `DiagInner`/`DiagCtxtInner` level rather than
1843                // the usual `Diag`/`DiagCtxt` level, so we must augment `bug`
1844                // in a lower-level fashion.
1845                bug.arg("level", bug.level);
1846                let msg = crate::fluent_generated::errors_invalid_flushed_delayed_diagnostic_level;
1847                let msg = self.eagerly_translate_for_subdiag(&bug, msg); // after the `arg` call
1848                bug.sub(Note, msg, bug.span.primary_span().unwrap().into());
1849            }
1850            bug.level = Bug;
1851
1852            self.emit_diagnostic(bug, None);
1853        }
1854
1855        // Panic with `DelayedBugPanic` to avoid "unexpected panic" messages.
1856        panic::panic_any(DelayedBugPanic);
1857    }
1858
1859    fn panic_if_treat_err_as_bug(&self) {
1860        if self.treat_err_as_bug() {
1861            let n = self.flags.treat_err_as_bug.map(|c| c.get()).unwrap();
1862            assert_eq!(n, self.err_guars.len() + self.lint_err_guars.len());
1863            if n == 1 {
1864                panic!("aborting due to `-Z treat-err-as-bug=1`");
1865            } else {
1866                panic!("aborting after {n} errors due to `-Z treat-err-as-bug={n}`");
1867            }
1868        }
1869    }
1870}
1871
1872struct DelayedDiagInner {
1873    inner: DiagInner,
1874    note: Backtrace,
1875}
1876
1877impl DelayedDiagInner {
1878    fn with_backtrace(diagnostic: DiagInner, backtrace: Backtrace) -> Self {
1879        DelayedDiagInner { inner: diagnostic, note: backtrace }
1880    }
1881
1882    fn decorate(self, dcx: &DiagCtxtInner) -> DiagInner {
1883        // We are at the `DiagInner`/`DiagCtxtInner` level rather than the
1884        // usual `Diag`/`DiagCtxt` level, so we must construct `diag` in a
1885        // lower-level fashion.
1886        let mut diag = self.inner;
1887        let msg = match self.note.status() {
1888            BacktraceStatus::Captured => crate::fluent_generated::errors_delayed_at_with_newline,
1889            // Avoid the needless newline when no backtrace has been captured,
1890            // the display impl should just be a single line.
1891            _ => crate::fluent_generated::errors_delayed_at_without_newline,
1892        };
1893        diag.arg("emitted_at", diag.emitted_at.clone());
1894        diag.arg("note", self.note);
1895        let msg = dcx.eagerly_translate_for_subdiag(&diag, msg); // after the `arg` calls
1896        diag.sub(Note, msg, diag.span.primary_span().unwrap_or(DUMMY_SP).into());
1897        diag
1898    }
1899}
1900
1901/// | Level        | is_error | EmissionGuarantee            | Top-level | Sub | Used in lints?
1902/// | -----        | -------- | -----------------            | --------- | --- | --------------
1903/// | Bug          | yes      | BugAbort                     | yes       | -   | -
1904/// | Fatal        | yes      | FatalAbort/FatalError[^star] | yes       | -   | -
1905/// | Error        | yes      | ErrorGuaranteed              | yes       | -   | yes
1906/// | DelayedBug   | yes      | ErrorGuaranteed              | yes       | -   | -
1907/// | ForceWarning | -        | ()                           | yes       | -   | lint-only
1908/// | Warning      | -        | ()                           | yes       | yes | yes
1909/// | Note         | -        | ()                           | rare      | yes | -
1910/// | OnceNote     | -        | ()                           | -         | yes | lint-only
1911/// | Help         | -        | ()                           | rare      | yes | -
1912/// | OnceHelp     | -        | ()                           | -         | yes | lint-only
1913/// | FailureNote  | -        | ()                           | rare      | -   | -
1914/// | Allow        | -        | ()                           | yes       | -   | lint-only
1915/// | Expect       | -        | ()                           | yes       | -   | lint-only
1916///
1917/// [^star]: `FatalAbort` normally, `FatalError` in the non-aborting "almost fatal" case that is
1918///     occasionally used.
1919///
1920#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug, Encodable, Decodable)]
1921pub enum Level {
1922    /// For bugs in the compiler. Manifests as an ICE (internal compiler error) panic.
1923    Bug,
1924
1925    /// An error that causes an immediate abort. Used for things like configuration errors,
1926    /// internal overflows, some file operation errors.
1927    Fatal,
1928
1929    /// An error in the code being compiled, which prevents compilation from finishing. This is the
1930    /// most common case.
1931    Error,
1932
1933    /// This is a strange one: lets you register an error without emitting it. If compilation ends
1934    /// without any other errors occurring, this will be emitted as a bug. Otherwise, it will be
1935    /// silently dropped. I.e. "expect other errors are emitted" semantics. Useful on code paths
1936    /// that should only be reached when compiling erroneous code.
1937    DelayedBug,
1938
1939    /// A `force-warn` lint warning about the code being compiled. Does not prevent compilation
1940    /// from finishing.
1941    ///
1942    /// Requires a [`LintExpectationId`] for expected lint diagnostics. In all other cases this
1943    /// should be `None`.
1944    ForceWarning,
1945
1946    /// A warning about the code being compiled. Does not prevent compilation from finishing.
1947    /// Will be skipped if `can_emit_warnings` is false.
1948    Warning,
1949
1950    /// A message giving additional context.
1951    Note,
1952
1953    /// A note that is only emitted once.
1954    OnceNote,
1955
1956    /// A message suggesting how to fix something.
1957    Help,
1958
1959    /// A help that is only emitted once.
1960    OnceHelp,
1961
1962    /// Similar to `Note`, but used in cases where compilation has failed. When printed for human
1963    /// consumption, it doesn't have any kind of `note:` label.
1964    FailureNote,
1965
1966    /// Only used for lints.
1967    Allow,
1968
1969    /// Only used for lints. Requires a [`LintExpectationId`] for silencing the lints.
1970    Expect,
1971}
1972
1973impl fmt::Display for Level {
1974    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1975        self.to_str().fmt(f)
1976    }
1977}
1978
1979impl Level {
1980    fn color(self) -> ColorSpec {
1981        let mut spec = ColorSpec::new();
1982        match self {
1983            Bug | Fatal | Error | DelayedBug => {
1984                spec.set_fg(Some(Color::Red)).set_intense(true);
1985            }
1986            ForceWarning | Warning => {
1987                spec.set_fg(Some(Color::Yellow)).set_intense(cfg!(windows));
1988            }
1989            Note | OnceNote => {
1990                spec.set_fg(Some(Color::Green)).set_intense(true);
1991            }
1992            Help | OnceHelp => {
1993                spec.set_fg(Some(Color::Cyan)).set_intense(true);
1994            }
1995            FailureNote => {}
1996            Allow | Expect => unreachable!(),
1997        }
1998        spec
1999    }
2000
2001    pub fn to_str(self) -> &'static str {
2002        match self {
2003            Bug | DelayedBug => "error: internal compiler error",
2004            Fatal | Error => "error",
2005            ForceWarning | Warning => "warning",
2006            Note | OnceNote => "note",
2007            Help | OnceHelp => "help",
2008            FailureNote => "failure-note",
2009            Allow | Expect => unreachable!(),
2010        }
2011    }
2012
2013    pub fn is_failure_note(&self) -> bool {
2014        matches!(*self, FailureNote)
2015    }
2016
2017    // Can this level be used in a subdiagnostic message?
2018    fn can_be_subdiag(&self) -> bool {
2019        match self {
2020            Bug | DelayedBug | Fatal | Error | ForceWarning | FailureNote | Allow | Expect => false,
2021
2022            Warning | Note | Help | OnceNote | OnceHelp => true,
2023        }
2024    }
2025}
2026
2027// FIXME(eddyb) this doesn't belong here AFAICT, should be moved to callsite.
2028pub fn elided_lifetime_in_path_suggestion(
2029    source_map: &SourceMap,
2030    n: usize,
2031    path_span: Span,
2032    incl_angl_brckt: bool,
2033    insertion_span: Span,
2034) -> ElidedLifetimeInPathSubdiag {
2035    let expected = ExpectedLifetimeParameter { span: path_span, count: n };
2036    // Do not try to suggest anything if generated by a proc-macro.
2037    let indicate = source_map.is_span_accessible(insertion_span).then(|| {
2038        let anon_lts = vec!["'_"; n].join(", ");
2039        let suggestion =
2040            if incl_angl_brckt { format!("<{anon_lts}>") } else { format!("{anon_lts}, ") };
2041
2042        IndicateAnonymousLifetime { span: insertion_span.shrink_to_hi(), count: n, suggestion }
2043    });
2044
2045    ElidedLifetimeInPathSubdiag { expected, indicate }
2046}
2047
2048pub fn report_ambiguity_error<'a, G: EmissionGuarantee>(
2049    diag: &mut Diag<'a, G>,
2050    ambiguity: rustc_lint_defs::AmbiguityErrorDiag,
2051) {
2052    diag.span_label(ambiguity.label_span, ambiguity.label_msg);
2053    diag.note(ambiguity.note_msg);
2054    diag.span_note(ambiguity.b1_span, ambiguity.b1_note_msg);
2055    for help_msg in ambiguity.b1_help_msgs {
2056        diag.help(help_msg);
2057    }
2058    diag.span_note(ambiguity.b2_span, ambiguity.b2_note_msg);
2059    for help_msg in ambiguity.b2_help_msgs {
2060        diag.help(help_msg);
2061    }
2062}
2063
2064/// Grammatical tool for displaying messages to end users in a nice form.
2065///
2066/// Returns "an" if the given string starts with a vowel, and "a" otherwise.
2067pub fn a_or_an(s: &str) -> &'static str {
2068    let mut chars = s.chars();
2069    let Some(mut first_alpha_char) = chars.next() else {
2070        return "a";
2071    };
2072    if first_alpha_char == '`' {
2073        let Some(next) = chars.next() else {
2074            return "a";
2075        };
2076        first_alpha_char = next;
2077    }
2078    if ["a", "e", "i", "o", "u", "&"].contains(&&first_alpha_char.to_lowercase().to_string()[..]) {
2079        "an"
2080    } else {
2081        "a"
2082    }
2083}
2084
2085#[derive(Clone, Copy, PartialEq, Hash, Debug)]
2086pub enum TerminalUrl {
2087    No,
2088    Yes,
2089    Auto,
2090}
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