1
Fork 0

macros: add diagnostic derive for lints

`SessionDiagnostic` isn't suitable for use on lints as whether or not it
creates an error or a warning is decided at compile-time by the macro,
whereas lints decide this at runtime based on the location of the lint
being reported (as it will depend on the user's `allow`/`deny`
attributes, etc). Re-using most of the machinery for
`SessionDiagnostic`, this macro introduces a `LintDiagnostic` derive
which implements a `DecorateLint` trait, taking a
`LintDiagnosticBuilder` and adding to the lint according to the
diagnostic struct.
This commit is contained in:
David Wood 2022-06-30 08:57:45 +01:00
parent 7f9d8480d6
commit 9d864c8d56
12 changed files with 847 additions and 614 deletions

View file

@ -1,10 +1,11 @@
mod diagnostic;
mod diagnostic_builder;
mod error;
mod fluent;
mod subdiagnostic;
mod utils;
use diagnostic::SessionDiagnosticDerive;
use diagnostic::{LintDiagnosticDerive, SessionDiagnosticDerive};
pub(crate) use fluent::fluent_messages;
use proc_macro2::TokenStream;
use quote::format_ident;
@ -58,11 +59,53 @@ use synstructure::Structure;
/// See rustc dev guide for more examples on using the `#[derive(SessionDiagnostic)]`:
/// <https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-structs.html>
pub fn session_diagnostic_derive(s: Structure<'_>) -> TokenStream {
// Names for the diagnostic we build and the session we build it from.
let diag = format_ident!("diag");
let sess = format_ident!("sess");
SessionDiagnosticDerive::new(format_ident!("diag"), format_ident!("sess"), s).into_tokens()
}
SessionDiagnosticDerive::new(diag, sess, 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)]
/// #[lint(lint::atomic_ordering_invalid_fail_success)]
/// pub struct AtomicOrderingInvalidLint {
/// method: Symbol,
/// success_ordering: Symbol,
/// fail_ordering: Symbol,
/// #[label(lint::fail_label)]
/// fail_order_arg_span: Span,
/// #[label(lint::success_label)]
/// #[suggestion(
/// code = "std::sync::atomic::Ordering::{success_suggestion}",
/// applicability = "maybe-incorrect"
/// )]
/// success_order_arg_span: Span,
/// }
/// ```
///
/// ```fluent
/// 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/sessiondiagnostic.html>
pub fn lint_diagnostic_derive(s: Structure<'_>) -> TokenStream {
LintDiagnosticDerive::new(format_ident!("diag"), s).into_tokens()
}
/// Implements `#[derive(SessionSubdiagnostic)]`, which allows for labels, notes, helps and