Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_errors/src/decorate_diag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use rustc_lint_defs::{BuiltinLintDiag, Lint, LintId};

use crate::{Diag, DiagCtxtHandle, Diagnostic, Level};

/// We can't implement `LintDiagnostic` for `BuiltinLintDiag`, because decorating some of its
/// We can't implement `Diagnostic` for `BuiltinLintDiag`, because decorating some of its
/// variants requires types we don't have yet. So, handle that case separately.
pub enum DecorateDiagCompat {
Dynamic(Box<dyn for<'a> FnOnce(DiagCtxtHandle<'a>, Level) -> Diag<'a, ()> + DynSend + 'static>),
Expand Down
8 changes: 0 additions & 8 deletions compiler/rustc_errors/src/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,14 +138,6 @@ where
fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>);
}

/// Trait implemented by lint types. This should not be implemented manually. Instead, use
/// `#[derive(LintDiagnostic)]` -- see [rustc_macros::LintDiagnostic].
#[rustc_diagnostic_item = "LintDiagnostic"]
pub trait LintDiagnostic<'a, G: EmissionGuarantee> {
/// Decorate a lint with the information from this type.
fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>);
}

#[derive(Clone, Debug, Encodable, Decodable)]
pub(crate) struct DiagLocation {
file: Cow<'static, str>,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_errors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub use codes::*;
pub use decorate_diag::{BufferedEarlyLint, DecorateDiagCompat, LintBuffer};
pub use diagnostic::{
BugAbort, Diag, DiagInner, DiagStyledString, Diagnostic, EmissionGuarantee, FatalAbort,
LintDiagnostic, StringPart, Subdiag, Subdiagnostic,
StringPart, Subdiag, Subdiagnostic,
};
pub use diagnostic_impls::{
DiagSymbolList, ElidedLifetimeInPathSubdiag, ExpectedLifetimeParameter,
Expand Down
54 changes: 2 additions & 52 deletions compiler/rustc_macros/src/diagnostics/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use proc_macro2::TokenStream;
use quote::quote;
use synstructure::Structure;

use crate::diagnostics::diagnostic_builder::DiagnosticDeriveKind;
use crate::diagnostics::diagnostic_builder::each_variant;
use crate::diagnostics::error::DiagnosticDeriveError;

/// The central struct for constructing the `into_diag` method from an annotated struct.
Expand All @@ -19,8 +19,7 @@ impl<'a> DiagnosticDerive<'a> {

pub(crate) fn into_tokens(self) -> TokenStream {
let DiagnosticDerive { mut structure } = self;
let kind = DiagnosticDeriveKind::Diagnostic;
let implementation = kind.each_variant(&mut structure, |mut builder, variant| {
let implementation = each_variant(&mut structure, |mut builder, variant| {
let preamble = builder.preamble(variant);
let body = builder.body(variant);

Expand Down Expand Up @@ -64,52 +63,3 @@ impl<'a> DiagnosticDerive<'a> {
})
}
}

/// The central struct for constructing the `decorate_lint` method from an annotated struct.
pub(crate) struct LintDiagnosticDerive<'a> {
structure: Structure<'a>,
}

impl<'a> LintDiagnosticDerive<'a> {
pub(crate) fn new(structure: Structure<'a>) -> Self {
Self { structure }
}

pub(crate) fn into_tokens(self) -> TokenStream {
let LintDiagnosticDerive { mut structure } = self;
let kind = DiagnosticDeriveKind::LintDiagnostic;
let implementation = kind.each_variant(&mut structure, |mut builder, variant| {
let preamble = builder.preamble(variant);
let body = builder.body(variant);

let Some(message) = builder.primary_message() else {
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
};
let message = message.diag_message(Some(variant));
let primary_message = quote! {
diag.primary_message(#message);
};

let formatting_init = &builder.formatting_init;
quote! {
#primary_message
#preamble
#formatting_init
#body
diag
}
});

structure.gen_impl(quote! {
gen impl<'__a> rustc_errors::LintDiagnostic<'__a, ()> for @Self {
#[track_caller]
fn decorate_lint<'__b>(
self,
diag: &'__b mut rustc_errors::Diag<'__a, ()>
) {
#implementation;
}
}
})
}
}
125 changes: 48 additions & 77 deletions compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,54 @@ use crate::diagnostics::utils::{
should_generate_arg, type_is_bool, type_is_unit, type_matches_path,
};

/// What kind of diagnostic is being derived - a fatal/error/warning or a lint?
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) enum DiagnosticDeriveKind {
Diagnostic,
LintDiagnostic,
pub(crate) fn each_variant<'s, F>(structure: &mut Structure<'s>, f: F) -> TokenStream
where
F: for<'v> Fn(DiagnosticDeriveVariantBuilder, &VariantInfo<'v>) -> TokenStream,
{
let ast = structure.ast();
let span = ast.span().unwrap();
match ast.data {
syn::Data::Struct(..) | syn::Data::Enum(..) => (),
syn::Data::Union(..) => {
span_err(span, "diagnostic derives can only be used on structs and enums").emit();
}
}

if matches!(ast.data, syn::Data::Enum(..)) {
for attr in &ast.attrs {
span_err(attr.span().unwrap(), "unsupported type attribute for diagnostic derive enum")
.emit();
}
}

structure.bind_with(|_| synstructure::BindStyle::Move);
let variants = structure.each_variant(|variant| {
let span = match structure.ast().data {
syn::Data::Struct(..) => span,
// There isn't a good way to get the span of the variant, so the variant's
// name will need to do.
_ => variant.ast().ident.span().unwrap(),
};
let builder = DiagnosticDeriveVariantBuilder {
span,
field_map: build_field_mapping(variant),
formatting_init: TokenStream::new(),
message: None,
code: None,
};
f(builder, variant)
});

quote! {
match self {
#variants
}
}
}

/// Tracks persistent information required for a specific variant when building up individual calls
/// to diagnostic methods for generated diagnostic derives - both `Diagnostic` for
/// fatal/errors/warnings and `LintDiagnostic` for lints.
/// to diagnostic methods for generated diagnostic derives.
pub(crate) struct DiagnosticDeriveVariantBuilder {
/// The kind for the entire type.
pub kind: DiagnosticDeriveKind,

/// Initialization of format strings for code suggestions.
pub formatting_init: TokenStream,

Expand All @@ -51,60 +85,6 @@ pub(crate) struct DiagnosticDeriveVariantBuilder {
pub code: SpannedOption<()>,
}

impl DiagnosticDeriveKind {
/// Call `f` for the struct or for each variant of the enum, returning a `TokenStream` with the
/// tokens from `f` wrapped in an `match` expression. Emits errors for use of derive on unions
/// or attributes on the type itself when input is an enum.
pub(crate) fn each_variant<'s, F>(self, structure: &mut Structure<'s>, f: F) -> TokenStream
where
F: for<'v> Fn(DiagnosticDeriveVariantBuilder, &VariantInfo<'v>) -> TokenStream,
{
let ast = structure.ast();
let span = ast.span().unwrap();
match ast.data {
syn::Data::Struct(..) | syn::Data::Enum(..) => (),
syn::Data::Union(..) => {
span_err(span, "diagnostic derives can only be used on structs and enums").emit();
}
}

if matches!(ast.data, syn::Data::Enum(..)) {
for attr in &ast.attrs {
span_err(
attr.span().unwrap(),
"unsupported type attribute for diagnostic derive enum",
)
.emit();
}
}

structure.bind_with(|_| synstructure::BindStyle::Move);
let variants = structure.each_variant(|variant| {
let span = match structure.ast().data {
syn::Data::Struct(..) => span,
// There isn't a good way to get the span of the variant, so the variant's
// name will need to do.
_ => variant.ast().ident.span().unwrap(),
};
let builder = DiagnosticDeriveVariantBuilder {
kind: self,
span,
field_map: build_field_mapping(variant),
formatting_init: TokenStream::new(),
message: None,
code: None,
};
f(builder, variant)
});

quote! {
match self {
#variants
}
}
}
}

impl DiagnosticDeriveVariantBuilder {
pub(crate) fn primary_message(&self) -> Option<&Message> {
match self.message.as_ref() {
Expand Down Expand Up @@ -358,20 +338,11 @@ impl DiagnosticDeriveVariantBuilder {
// `arg` call will not be generated.
(Meta::Path(_), "skip_arg") => return Ok(quote! {}),
(Meta::Path(_), "primary_span") => {
match self.kind {
DiagnosticDeriveKind::Diagnostic => {
report_error_if_not_applied_to_span(attr, &info)?;
report_error_if_not_applied_to_span(attr, &info)?;

return Ok(quote! {
diag.span(#binding);
});
}
DiagnosticDeriveKind::LintDiagnostic => {
throw_invalid_attr!(attr, |diag| {
diag.help("the `primary_span` field attribute is not valid for lint diagnostics")
})
}
}
return Ok(quote! {
diag.span(#binding);
});
}
(Meta::Path(_), "subdiagnostic") => {
return Ok(quote! { diag.subdiagnostic(#binding); });
Expand Down
36 changes: 2 additions & 34 deletions compiler/rustc_macros/src/diagnostics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ mod msg_macro;
mod subdiagnostic;
mod utils;

use diagnostic::{DiagnosticDerive, LintDiagnosticDerive};
use diagnostic::DiagnosticDerive;
pub(super) use msg_macro::msg_macro;
use proc_macro2::TokenStream;
use subdiagnostic::SubdiagnosticDerive;
Expand Down Expand Up @@ -51,38 +51,6 @@ pub(super) fn diagnostic_derive(s: Structure<'_>) -> TokenStream {
DiagnosticDerive::new(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)]
/// #[diag("unused attribute")]
/// pub(crate) struct UnusedAttribute {
/// #[suggestion("remove this attribute", code = "", applicability = "machine-applicable")]
/// pub this: Span,
/// #[note("attribute also specified here")]
/// pub other: Span,
/// #[warning(
/// "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!"
/// )]
/// pub warning: bool,
/// }
/// ```
///
/// Then, later, to emit the error:
///
/// ```ignore (rust)
/// cx.emit_span_lint(UNUSED_ATTRIBUTES, span, UnusedAttribute {
/// ...
/// });
/// ```
///
/// 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(super) fn lint_diagnostic_derive(s: Structure<'_>) -> TokenStream {
LintDiagnosticDerive::new(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.
Expand All @@ -99,7 +67,7 @@ pub(super) fn lint_diagnostic_derive(s: Structure<'_>) -> TokenStream {
/// Then, later, use the subdiagnostic in a diagnostic:
///
/// ```ignore (rust)
/// #[derive(LintDiagnostic)]
/// #[derive(Diagnostic)]
/// #[diag("unused doc comment")]
/// pub(crate) struct BuiltinUnusedDocComment<'a> {
/// pub kind: &'a str,
Expand Down
19 changes: 0 additions & 19 deletions compiler/rustc_macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,25 +196,6 @@ decl_derive!(
suggestion_hidden,
suggestion_verbose)] => diagnostics::diagnostic_derive
);
decl_derive!(
[LintDiagnostic, attributes(
// struct attributes
diag,
help,
help_once,
note,
note_once,
warning,
// field attributes
skip_arg,
primary_span,
label,
subdiagnostic,
suggestion,
suggestion_short,
suggestion_hidden,
suggestion_verbose)] => diagnostics::lint_diagnostic_derive
);
decl_derive!(
[Subdiagnostic, attributes(
// struct/variant attributes
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_middle/src/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -492,8 +492,8 @@ pub fn lint_level(
/// - [`TyCtxt::node_lint`]
/// - `LintContext::opt_span_lint`
///
/// This function will replace `lint_level` once all `LintDiagnostic` items have been migrated to
/// `Diagnostic`.
/// This function will replace `lint_level` once all its callers have been replaced
/// with `diag_lint_level`.
#[track_caller]
pub fn diag_lint_level<'a, D: Diagnostic<'a, ()> + 'a>(
sess: &'a Session,
Expand Down
Loading
Loading