This document describes the error handling and diagnostic system in the Rust compiler. This system is responsible for collecting, managing, formatting, and emitting all compiler diagnostics including errors, warnings, notes, and suggestions. The diagnostic system provides type-safe guarantees that errors are handled appropriately and supports multiple output formats (human-readable, JSON, annotate-snippets).
For information about specific diagnostic messages in compiler passes, see the individual pass documentation. For session configuration, see Compiler Driver and Session Management.
The diagnostic system is centered around the DiagCtxt (Diagnostic Context) which manages the lifecycle of all diagnostics during compilation. Diagnostics flow from creation through potential stashing and modification, to final emission through pluggable emitters.
Sources: compiler/rustc_errors/src/lib.rs273-370 compiler/rustc_errors/src/diagnostic.rs1-135 compiler/rustc_errors/src/emitter.rs52-93
DiagCtxt is the central type that manages all diagnostics during compilation. It tracks emitted errors, delayed bugs, stashed diagnostics, and provides the interface for creating and emitting diagnostics.
| Component | Type | Purpose |
|---|---|---|
err_guars | Vec<ErrorGuaranteed> | Tracks all emitted errors with their guarantees |
lint_err_guars | Vec<ErrorGuaranteed> | Tracks lint errors separately |
delayed_bugs | Vec<(DelayedDiagInner, ErrorGuaranteed)> | Bugs delayed until end of compilation |
stashed_diagnostics | FxIndexMap<StashKey, ...> | Diagnostics held for potential improvement |
emitter | Box<DynEmitter> | Pluggable output formatter |
emitted_diagnostics | FxHashSet<Hash128> | Deduplication tracking |
deduplicated_err_count | usize | Count shown to user |
Sources: compiler/rustc_errors/src/lib.rs299-370
DiagInner is the internal, heap-allocated representation of a diagnostic: it holds the level, messages, spans, children, suggestions, and error code. It is not generic and has no lifetime. Diag<'a, G> is the public builder type that wraps DiagInner and carries a lifetime 'a (tying it to the DiagCtxtHandle) and a type parameter G: EmissionGuarantee that determines what emit() returns.
The Diagnostic trait (derivable via #[derive(Diagnostic)] from rustc_macros) converts a structured error struct into a Diag<'a, G> via into_diag(). This allows error types to be defined declaratively with span fields and Fluent message attributes.
Sources: compiler/rustc_errors/src/diagnostic.rs86-200 compiler/rustc_errors/src/lib.rs41-44
DiagCtxtHandle provides a handle to DiagCtxt that can optionally track tainted state for error propagation:
Sources: compiler/rustc_errors/src/lib.rs280-286 compiler/rustc_errors/src/lib.rs579-591
The Level enum determines how diagnostics are processed and displayed. The full set of levels visible in DiagCtxtInner::stash_diagnostic is:
| Level | Purpose | Aborts Compilation |
|---|---|---|
Bug | Internal compiler errors (explicit ICE call) | Yes (panic_any(ExplicitBug)) |
Fatal | Unrecoverable errors (stops compilation immediately) | Yes (FatalError.raise()) |
Error | Compilation errors counted toward error total | After all phases |
DelayedBug | ICEs deferred to avoid cascading; emitted at end | Yes (if no hard errors replace it) |
ForceWarning | Warnings that bypass --cap-lints and --allow | No |
Warning | Standard warnings | No |
Note | Informational notes attached to a parent diagnostic | No |
OnceNote | Like Note, but deduplicated to emit only once | No |
Help | Helpful suggestions attached to a parent diagnostic | No |
OnceHelp | Like Help, but deduplicated to emit only once | No |
FailureNote | End-of-compilation summary notes (e.g., "aborting due to N errors") | No |
Allow | Suppressed diagnostics (lint allowed) | No |
Expect | Expected lint diagnostic; fulfills a #[expect(lint)] | No |
Sources: compiler/rustc_errors/src/lib.rs604-620
Diagnostics are created using a builder pattern through the Diag<'a, G> type, where G is an EmissionGuarantee:
Sources: compiler/rustc_errors/src/diagnostic.rs136-400 compiler/rustc_errors/src/lib.rs948-1081
The diagnostic system uses the type system to ensure errors are handled correctly through the EmissionGuarantee trait and ErrorGuaranteed token:
The ErrorGuaranteed type is a zero-sized type that serves as a proof token that an error diagnostic was emitted. This prevents the compiler from continuing in invalid states:
Sources: compiler/rustc_errors/src/diagnostic.rs26-86 compiler/rustc_span/src/lib.rs2329-2345
Diagnostic messages in rustc use the Fluent localization system, managed by the rustc_error_messages crate. This allows messages to be separated from code and supports argument interpolation.
| Type | Crate | Purpose |
|---|---|---|
DiagMessage | rustc_error_messages | A message that is either a plain Str(Cow<'static, str>) or a Fluent identifier |
DiagArgName | rustc_error_messages | Interned name of a Fluent argument |
DiagArgValue | rustc_error_messages | Value of a Fluent argument (Str, Number, or StrListSepByAnd) |
DiagArgMap | rustc_errors | Alias for FxIndexMap<DiagArgName, DiagArgValue> |
IntoDiagArg | rustc_error_messages | Trait for converting Rust values to DiagArgValue |
FluentArgs | re-exported from fluent_bundle | Argument map passed to Fluent for rendering |
How translation works: When a DiagInner is emitted, the Emitter calls format_diag_message (in compiler/rustc_errors/src/translation.rs) with the DiagMessage and a FluentArgs map built from the diagnostic's args field. For plain Str messages, no lookup is needed. For Fluent identifiers, the message template is fetched from the loaded Fluent bundle and arguments are substituted.
The msg! macro (re-exported from rustc_macros) creates DiagMessage values from string literals. The eagerly_translate and eagerly_translate_to_string methods on DiagCtxt perform translation outside of emission.
Structured error types use #[derive(Diagnostic)] from rustc_macros. Fields annotated with #[primary_span], #[label], #[note], #[help], #[suggestion], etc. are automatically mapped to spans, labels, and suggestions. The macro generates a Diagnostic impl that builds a Diag by calling DiagCtxtHandle builder methods.
Sources: compiler/rustc_error_messages/src/lib.rs1-50 compiler/rustc_errors/src/lib.rs55-62 compiler/rustc_errors/src/lib.rs72-73 compiler/rustc_errors/src/emitter.rs27-28
Emitters are responsible for formatting and outputting diagnostics. The system uses trait objects to support pluggable output formats:
Sources: compiler/rustc_errors/src/emitter.rs52-296 compiler/rustc_errors/src/json.rs1-500 compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs1-200
The human-readable emitter formats diagnostics for terminal display with colors, Unicode characters, and contextual source code snippets. It handles:
anstream/anstyle^) annotationsMultiSpanfix_multispans_in_extern_macros_and_render_macro_backtraceSources: compiler/rustc_errors/src/emitter.rs156-296
AnnotateSnippetEmitter in compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs implements Emitter using the external annotate-snippets crate. It converts DiagInner into annotate_snippets::Snippet structures and delegates rendering to that library. It is activated when --error-format=human is used together with -Zunstable-options and a relevant flag, or configured through AnnotateSnippetEmitter::new() in rustc_session. It is also used as the alternative renderer in rustdoc.
Sources: compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs1-50 compiler/rustc_session/src/session.rs14 compiler/rustc_session/src/parse.rs11
The JSON emitter produces machine-readable structured output used by tools like rust-analyzer, cargo, and IDEs:
| Field | Type | Purpose |
|---|---|---|
message | String | Main diagnostic message |
code | Option<DiagnosticCode> | Error code (e.g., E0308) |
level | String | "error", "warning", etc. |
spans | Vec<DiagnosticSpan> | Source locations |
children | Vec<Diagnostic> | Sub-diagnostics |
rendered | Option<String> | Human-readable format |
The JSON emitter also supports additional notification types beyond diagnostics: artifact notifications (emit_artifact_notification), timing section events (emit_timing_section), future-breakage reports (emit_future_breakage_report), and unused-extern reports (emit_unused_externs). These are only available in JSON mode and are used by Cargo and other tooling.
Sources: compiler/rustc_errors/src/json.rs1-50 compiler/rustc_errors/src/emitter.rs53-88
The suggestion system allows the compiler to propose fixes to the user's code:
Suggestions are stored in a Suggestions enum that can be:
Enabled(Vec<CodeSuggestion>) - Normal state, suggestions can be addedSealed(Box<[CodeSuggestion]>) - Locked, no modifications allowedDisabled - No suggestions availableSources: compiler/rustc_errors/src/lib.rs127-243 compiler/rustc_errors/src/diagnostic.rs700-900
The stashing mechanism allows diagnostics to be temporarily held for potential improvement in later compilation stages:
When stashing an error-level diagnostic, a DelayedBug is created to ensure the error count increases immediately. This maintains the invariant that ErrorGuaranteed means an error was (or will be) shown to the user.
Sources: compiler/rustc_errors/src/lib.rs616-718 compiler/rustc_errors/src/lib.rs372-396
Delayed bugs are ICEs (Internal Compiler Errors) that are held until the end of compilation. They allow the compiler to collect multiple errors before aborting:
Delayed bugs are useful for:
-Ztreat-err-as-bug to get backtraces at specific error countsSources: compiler/rustc_errors/src/lib.rs428-467 compiler/rustc_errors/src/lib.rs932-935
The TRACK_DIAGNOSTIC static in compiler/rustc_errors/src/lib.rs is an AtomicRef function pointer that wraps every diagnostic emission. Incremental compilation installs its own function pointer here to replay diagnostics from a previous session without re-running the full compiler pass. By default the function pointer is default_track_diagnostic, which simply calls the closure immediately.
Sources: compiler/rustc_errors/src/lib.rs391-395
The diagnostic system integrates throughout the compilation pipeline:
Sources: compiler/rustc_driver_impl/src/lib.rs218-350 compiler/rustc_session/src/session.rs239-248 compiler/rustc_interface/src/passes.rs399-420
Session provides access to the diagnostic context through dcx(), which returns a DiagCtxtHandle<'_> pointing at the DiagCtxt inside ParseSess:
finish_diagnostics() on Session is the canonical end-of-session cleanup: it emits stashed diagnostics, prints the error count summary, and optionally emits a future-breakage JSON report.
Sources: compiler/rustc_session/src/session.rs284-288 compiler/rustc_session/src/session.rs239-248
Before the full Session is available, EarlyDiagCtxt provides basic diagnostic capabilities for:
EarlyDiagCtxt is constructed with an ErrorOutputType in run_compiler and is used for argument expansion and option parsing. Once the Session is created, callers drop the EarlyDiagCtxt and use sess.dcx() thereafter.
Sources: compiler/rustc_driver_impl/src/lib.rs171-204 compiler/rustc_session/src/parse.rs1-50
Diagnostic behavior is configured through several mechanisms:
| Configuration | Type | Purpose |
|---|---|---|
ErrorOutputType | Enum | Human-readable vs JSON output |
DiagCtxtFlags | Struct | Behavioral flags |
ColorConfig | Enum | Color output settings |
diagnostic_width | Option<usize> | Terminal width for formatting |
Sources: compiler/rustc_errors/src/lib.rs408-426 compiler/rustc_session/src/config.rs1-100
Emitters are created based on ErrorOutputType:
Sources: compiler/rustc_session/src/config.rs807-825 compiler/rustc_session/src/session.rs1-150
Sources: compiler/rustc_errors/src/lib.rs616-718
Sources: compiler/rustc_errors/src/diagnostic.rs26-86
Sources: compiler/rustc_errors/src/lib.rs1-1000 compiler/rustc_errors/src/diagnostic.rs1-1000 compiler/rustc_errors/src/emitter.rs1-500 compiler/rustc_errors/src/json.rs1-500 compiler/rustc_session/src/session.rs1-300 compiler/rustc_session/src/config.rs1-200
Refresh this wiki
This wiki was recently refreshed. Please wait 4 days to refresh again.