rust/compiler/rustc_macros/src/diagnostics/mod.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

160 lines
5.0 KiB
Rust
Raw Normal View History

mod diagnostic;
mod diagnostic_builder;
mod error;
macros: introduce `fluent_messages` macro Adds a new `fluent_messages` macro which performs compile-time validation of the compiler's Fluent resources (i.e. that the resources parse and don't multiply define the same messages) and generates constants that make using those messages in diagnostics more ergonomic. For example, given the following invocation of the macro.. ```ignore (rust) fluent_messages! { typeck => "./typeck.ftl", } ``` ..where `typeck.ftl` has the following contents.. ```fluent typeck-field-multiply-specified-in-initializer = field `{$ident}` specified more than once .label = used more than once .label-previous-use = first use of `{$ident}` ``` ...then the macro parse the Fluent resource, emitting a diagnostic if it fails to do so, and will generate the following code: ```ignore (rust) pub static DEFAULT_LOCALE_RESOURCES: &'static [&'static str] = &[ include_str!("./typeck.ftl"), ]; mod fluent_generated { mod typeck { pub const field_multiply_specified_in_initializer: DiagnosticMessage = DiagnosticMessage::fluent("typeck-field-multiply-specified-in-initializer"); pub const field_multiply_specified_in_initializer_label_previous_use: DiagnosticMessage = DiagnosticMessage::fluent_attr( "typeck-field-multiply-specified-in-initializer", "previous-use-label" ); } } ``` When emitting a diagnostic, the generated constants can be used as follows: ```ignore (rust) let mut err = sess.struct_span_err( span, fluent::typeck::field_multiply_specified_in_initializer ); err.span_default_label(span); err.span_label( previous_use_span, fluent::typeck::field_multiply_specified_in_initializer_label_previous_use ); err.emit(); ``` Signed-off-by: David Wood <david.wood@huawei.com>
2022-05-23 17:24:55 +00:00
mod fluent;
mod subdiagnostic;
mod utils;
use diagnostic::{DiagnosticDerive, LintDiagnosticDerive};
macros: introduce `fluent_messages` macro Adds a new `fluent_messages` macro which performs compile-time validation of the compiler's Fluent resources (i.e. that the resources parse and don't multiply define the same messages) and generates constants that make using those messages in diagnostics more ergonomic. For example, given the following invocation of the macro.. ```ignore (rust) fluent_messages! { typeck => "./typeck.ftl", } ``` ..where `typeck.ftl` has the following contents.. ```fluent typeck-field-multiply-specified-in-initializer = field `{$ident}` specified more than once .label = used more than once .label-previous-use = first use of `{$ident}` ``` ...then the macro parse the Fluent resource, emitting a diagnostic if it fails to do so, and will generate the following code: ```ignore (rust) pub static DEFAULT_LOCALE_RESOURCES: &'static [&'static str] = &[ include_str!("./typeck.ftl"), ]; mod fluent_generated { mod typeck { pub const field_multiply_specified_in_initializer: DiagnosticMessage = DiagnosticMessage::fluent("typeck-field-multiply-specified-in-initializer"); pub const field_multiply_specified_in_initializer_label_previous_use: DiagnosticMessage = DiagnosticMessage::fluent_attr( "typeck-field-multiply-specified-in-initializer", "previous-use-label" ); } } ``` When emitting a diagnostic, the generated constants can be used as follows: ```ignore (rust) let mut err = sess.struct_span_err( span, fluent::typeck::field_multiply_specified_in_initializer ); err.span_default_label(span); err.span_label( previous_use_span, fluent::typeck::field_multiply_specified_in_initializer_label_previous_use ); err.emit(); ``` Signed-off-by: David Wood <david.wood@huawei.com>
2022-05-23 17:24:55 +00:00
pub(crate) use fluent::fluent_messages;
use proc_macro2::TokenStream;
use quote::format_ident;
use subdiagnostic::SubdiagnosticDeriveBuilder;
use synstructure::Structure;
/// Implements `#[derive(Diagnostic)]`, which allows for errors to be specified as a struct,
/// independent from the actual diagnostics emitting code.
///
/// ```ignore (rust)
/// # extern crate rustc_errors;
/// # use rustc_errors::Applicability;
/// # extern crate rustc_span;
/// # use rustc_span::{symbol::Ident, Span};
/// # extern crate rust_middle;
/// # use rustc_middle::ty::Ty;
/// #[derive(Diagnostic)]
2022-10-22 09:07:54 +00:00
/// #[diag(borrowck_move_out_of_borrow, code = "E0505")]
/// pub struct MoveOutOfBorrowError<'tcx> {
/// pub name: Ident,
/// pub ty: Ty<'tcx>,
/// #[primary_span]
/// #[label]
/// pub span: Span,
2022-10-22 09:07:54 +00:00
/// #[label(first_borrow_label)]
/// pub first_borrow_span: Span,
/// #[suggestion(code = "{name}.clone()")]
/// pub clone_sugg: Option<(Span, Applicability)>
/// }
/// ```
///
/// ```fluent
2022-08-10 09:31:10 +00:00
/// move_out_of_borrow = cannot move out of {$name} because it is borrowed
/// .label = cannot move out of borrow
2022-08-10 09:31:10 +00:00
/// .first_borrow_label = `{$ty}` first borrowed here
/// .suggestion = consider cloning here
/// ```
///
/// Then, later, to emit the error:
///
/// ```ignore (rust)
/// sess.emit_err(MoveOutOfBorrowError {
/// expected,
/// actual,
/// span,
/// first_borrow_span,
/// clone_sugg: Some(suggestion, Applicability::MachineApplicable),
/// });
/// ```
///
/// See rustc dev guide for more examples on using the `#[derive(Diagnostic)]`:
/// <https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-structs.html>
pub fn session_diagnostic_derive(s: Structure<'_>) -> TokenStream {
DiagnosticDerive::new(format_ident!("diag"), format_ident!("handler"), s).into_tokens()
}
/// Implements `#[derive(LintDiagnostic)]`, which allows for lints to be specified as a struct,
/// independent from the actual lint emitting code.
///
/// ```ignore (rust)
/// #[derive(LintDiagnostic)]
2022-10-22 09:07:54 +00:00
/// #[diag(lint_atomic_ordering_invalid_fail_success)]
/// pub struct AtomicOrderingInvalidLint {
/// method: Symbol,
/// success_ordering: Symbol,
/// fail_ordering: Symbol,
2022-10-22 09:07:54 +00:00
/// #[label(fail_label)]
/// fail_order_arg_span: Span,
2022-10-22 09:07:54 +00:00
/// #[label(success_label)]
/// #[suggestion(
/// code = "std::sync::atomic::Ordering::{success_suggestion}",
/// applicability = "maybe-incorrect"
/// )]
/// success_order_arg_span: Span,
/// }
/// ```
///
/// ```fluent
2022-08-10 09:31:10 +00:00
/// lint_atomic_ordering_invalid_fail_success = `{$method}`'s success ordering must be at least as strong as its failure ordering
/// .fail_label = `{$fail_ordering}` failure ordering
/// .success_label = `{$success_ordering}` success ordering
/// .suggestion = consider using `{$success_suggestion}` success ordering instead
/// ```
///
/// Then, later, to emit the error:
///
/// ```ignore (rust)
/// cx.struct_span_lint(INVALID_ATOMIC_ORDERING, fail_order_arg_span, AtomicOrderingInvalidLint {
/// method,
/// success_ordering,
/// fail_ordering,
/// fail_order_arg_span,
/// success_order_arg_span,
/// });
/// ```
///
/// See rustc dev guide for more examples on using the `#[derive(LintDiagnostic)]`:
/// <https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-structs.html#reference>
pub fn lint_diagnostic_derive(s: Structure<'_>) -> TokenStream {
LintDiagnosticDerive::new(format_ident!("diag"), s).into_tokens()
}
/// Implements `#[derive(Subdiagnostic)]`, which allows for labels, notes, helps and
/// suggestions to be specified as a structs or enums, independent from the actual diagnostics
/// emitting code or diagnostic derives.
///
/// ```ignore (rust)
/// #[derive(Subdiagnostic)]
/// pub enum ExpectedIdentifierLabel<'tcx> {
2022-10-22 09:07:54 +00:00
/// #[label(expected_identifier)]
/// WithoutFound {
/// #[primary_span]
/// span: Span,
/// }
2022-10-22 09:07:54 +00:00
/// #[label(expected_identifier_found)]
/// WithFound {
/// #[primary_span]
/// span: Span,
/// found: String,
/// }
/// }
///
/// #[derive(Subdiagnostic)]
/// #[suggestion(style = "verbose",parser::raw_identifier)]
/// pub struct RawIdentifierSuggestion<'tcx> {
/// #[primary_span]
/// span: Span,
/// #[applicability]
/// applicability: Applicability,
/// ident: Ident,
/// }
/// ```
///
/// ```fluent
2022-08-10 09:31:10 +00:00
/// parser_expected_identifier = expected identifier
///
2022-08-10 09:31:10 +00:00
/// parser_expected_identifier-found = expected identifier, found {$found}
///
2022-08-10 09:31:10 +00:00
/// parser_raw_identifier = escape `{$ident}` to use it as an identifier
/// ```
///
/// Then, later, to add the subdiagnostic:
///
/// ```ignore (rust)
/// diag.subdiagnostic(ExpectedIdentifierLabel::WithoutFound { span });
///
/// diag.subdiagnostic(RawIdentifierSuggestion { span, applicability, ident });
/// ```
pub fn session_subdiagnostic_derive(s: Structure<'_>) -> TokenStream {
SubdiagnosticDeriveBuilder::new().into_tokens(s)
}