use proc_macro2_diagnostics::Diagnostic; use quote::ToTokens; /// A collection of diagnostics /// /// This is a wrapper type since we want it to be transparent in terms of PartialEq and Eq. /// This also lets us choose the expansion strategy for the diagnostics. #[derive(Debug, Clone, Default)] pub struct Diagnostics { pub diagnostics: Vec, } impl Diagnostics { pub fn new() -> Self { Self { diagnostics: vec![], } } pub fn push(&mut self, diagnostic: Diagnostic) { self.diagnostics.push(diagnostic); } pub fn extend(&mut self, diagnostics: Vec) { self.diagnostics.extend(diagnostics); } pub fn is_empty(&self) -> bool { self.diagnostics.is_empty() } pub fn into_diagnostics(self) -> Vec { self.diagnostics } pub fn len(&self) -> usize { self.diagnostics.len() } } impl PartialEq for Diagnostics { fn eq(&self, _other: &Self) -> bool { true } } impl Eq for Diagnostics {} impl ToTokens for Diagnostics { fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { for diagnostic in &self.diagnostics { tokens.extend(diagnostic.clone().emit_as_expr_tokens()); } } } // TODO: Ideally this would be integrated into the existing diagnostics struct, but currently all fields are public so adding // new fields would be a breaking change. Diagnostics also doesn't expose the message directly so we can't just modify // the expansion pub(crate) mod new_diagnostics { use proc_macro2_diagnostics::SpanDiagnosticExt; use std::fmt::Display; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::quote_spanned; pub(crate) fn warning_diagnostic(span: Span, message: impl Display) -> TokenStream2 { let note = message.to_string(); // If we are compiling on nightly, use diagnostics directly which supports proper warnings through new span apis if rustversion::cfg!(nightly) { return span.warning(note).emit_as_item_tokens(); } quote_spanned! { span => const _: () = { #[deprecated(note = #note)] struct Warning; _ = Warning; }; } } }