1ea88969fSopenharmony_ciuse crate::{abort_now, check_correctness, sealed::Sealed, SpanRange}; 2ea88969fSopenharmony_ciuse proc_macro2::Span; 3ea88969fSopenharmony_ciuse proc_macro2::TokenStream; 4ea88969fSopenharmony_ci 5ea88969fSopenharmony_ciuse quote::{quote_spanned, ToTokens}; 6ea88969fSopenharmony_ci 7ea88969fSopenharmony_ci/// Represents a diagnostic level 8ea88969fSopenharmony_ci/// 9ea88969fSopenharmony_ci/// # Warnings 10ea88969fSopenharmony_ci/// 11ea88969fSopenharmony_ci/// Warnings are ignored on stable/beta 12ea88969fSopenharmony_ci#[derive(Debug, PartialEq)] 13ea88969fSopenharmony_cipub enum Level { 14ea88969fSopenharmony_ci Error, 15ea88969fSopenharmony_ci Warning, 16ea88969fSopenharmony_ci #[doc(hidden)] 17ea88969fSopenharmony_ci NonExhaustive, 18ea88969fSopenharmony_ci} 19ea88969fSopenharmony_ci 20ea88969fSopenharmony_ci/// Represents a single diagnostic message 21ea88969fSopenharmony_ci#[derive(Debug)] 22ea88969fSopenharmony_cipub struct Diagnostic { 23ea88969fSopenharmony_ci pub(crate) level: Level, 24ea88969fSopenharmony_ci pub(crate) span_range: SpanRange, 25ea88969fSopenharmony_ci pub(crate) msg: String, 26ea88969fSopenharmony_ci pub(crate) suggestions: Vec<(SuggestionKind, String, Option<SpanRange>)>, 27ea88969fSopenharmony_ci pub(crate) children: Vec<(SpanRange, String)>, 28ea88969fSopenharmony_ci} 29ea88969fSopenharmony_ci 30ea88969fSopenharmony_ci/// A collection of methods that do not exist in `proc_macro::Diagnostic` 31ea88969fSopenharmony_ci/// but still useful to have around. 32ea88969fSopenharmony_ci/// 33ea88969fSopenharmony_ci/// This trait is sealed and cannot be implemented outside of `proc_macro_error`. 34ea88969fSopenharmony_cipub trait DiagnosticExt: Sealed { 35ea88969fSopenharmony_ci /// Create a new diagnostic message that points to the `span_range`. 36ea88969fSopenharmony_ci /// 37ea88969fSopenharmony_ci /// This function is the same as `Diagnostic::spanned` but produces considerably 38ea88969fSopenharmony_ci /// better error messages for multi-token spans on stable. 39ea88969fSopenharmony_ci fn spanned_range(span_range: SpanRange, level: Level, message: String) -> Self; 40ea88969fSopenharmony_ci 41ea88969fSopenharmony_ci /// Add another error message to self such that it will be emitted right after 42ea88969fSopenharmony_ci /// the main message. 43ea88969fSopenharmony_ci /// 44ea88969fSopenharmony_ci /// This function is the same as `Diagnostic::span_error` but produces considerably 45ea88969fSopenharmony_ci /// better error messages for multi-token spans on stable. 46ea88969fSopenharmony_ci fn span_range_error(self, span_range: SpanRange, msg: String) -> Self; 47ea88969fSopenharmony_ci 48ea88969fSopenharmony_ci /// Attach a "help" note to your main message, the note will have it's own span on nightly. 49ea88969fSopenharmony_ci /// 50ea88969fSopenharmony_ci /// This function is the same as `Diagnostic::span_help` but produces considerably 51ea88969fSopenharmony_ci /// better error messages for multi-token spans on stable. 52ea88969fSopenharmony_ci /// 53ea88969fSopenharmony_ci /// # Span 54ea88969fSopenharmony_ci /// 55ea88969fSopenharmony_ci /// The span is ignored on stable, the note effectively inherits its parent's (main message) span 56ea88969fSopenharmony_ci fn span_range_help(self, span_range: SpanRange, msg: String) -> Self; 57ea88969fSopenharmony_ci 58ea88969fSopenharmony_ci /// Attach a note to your main message, the note will have it's own span on nightly. 59ea88969fSopenharmony_ci /// 60ea88969fSopenharmony_ci /// This function is the same as `Diagnostic::span_note` but produces considerably 61ea88969fSopenharmony_ci /// better error messages for multi-token spans on stable. 62ea88969fSopenharmony_ci /// 63ea88969fSopenharmony_ci /// # Span 64ea88969fSopenharmony_ci /// 65ea88969fSopenharmony_ci /// The span is ignored on stable, the note effectively inherits its parent's (main message) span 66ea88969fSopenharmony_ci fn span_range_note(self, span_range: SpanRange, msg: String) -> Self; 67ea88969fSopenharmony_ci} 68ea88969fSopenharmony_ci 69ea88969fSopenharmony_ciimpl DiagnosticExt for Diagnostic { 70ea88969fSopenharmony_ci fn spanned_range(span_range: SpanRange, level: Level, message: String) -> Self { 71ea88969fSopenharmony_ci Diagnostic { 72ea88969fSopenharmony_ci level, 73ea88969fSopenharmony_ci span_range, 74ea88969fSopenharmony_ci msg: message, 75ea88969fSopenharmony_ci suggestions: vec![], 76ea88969fSopenharmony_ci children: vec![], 77ea88969fSopenharmony_ci } 78ea88969fSopenharmony_ci } 79ea88969fSopenharmony_ci 80ea88969fSopenharmony_ci fn span_range_error(mut self, span_range: SpanRange, msg: String) -> Self { 81ea88969fSopenharmony_ci self.children.push((span_range, msg)); 82ea88969fSopenharmony_ci self 83ea88969fSopenharmony_ci } 84ea88969fSopenharmony_ci 85ea88969fSopenharmony_ci fn span_range_help(mut self, span_range: SpanRange, msg: String) -> Self { 86ea88969fSopenharmony_ci self.suggestions 87ea88969fSopenharmony_ci .push((SuggestionKind::Help, msg, Some(span_range))); 88ea88969fSopenharmony_ci self 89ea88969fSopenharmony_ci } 90ea88969fSopenharmony_ci 91ea88969fSopenharmony_ci fn span_range_note(mut self, span_range: SpanRange, msg: String) -> Self { 92ea88969fSopenharmony_ci self.suggestions 93ea88969fSopenharmony_ci .push((SuggestionKind::Note, msg, Some(span_range))); 94ea88969fSopenharmony_ci self 95ea88969fSopenharmony_ci } 96ea88969fSopenharmony_ci} 97ea88969fSopenharmony_ci 98ea88969fSopenharmony_ciimpl Diagnostic { 99ea88969fSopenharmony_ci /// Create a new diagnostic message that points to `Span::call_site()` 100ea88969fSopenharmony_ci pub fn new(level: Level, message: String) -> Self { 101ea88969fSopenharmony_ci Diagnostic::spanned(Span::call_site(), level, message) 102ea88969fSopenharmony_ci } 103ea88969fSopenharmony_ci 104ea88969fSopenharmony_ci /// Create a new diagnostic message that points to the `span` 105ea88969fSopenharmony_ci pub fn spanned(span: Span, level: Level, message: String) -> Self { 106ea88969fSopenharmony_ci Diagnostic::spanned_range( 107ea88969fSopenharmony_ci SpanRange { 108ea88969fSopenharmony_ci first: span, 109ea88969fSopenharmony_ci last: span, 110ea88969fSopenharmony_ci }, 111ea88969fSopenharmony_ci level, 112ea88969fSopenharmony_ci message, 113ea88969fSopenharmony_ci ) 114ea88969fSopenharmony_ci } 115ea88969fSopenharmony_ci 116ea88969fSopenharmony_ci /// Add another error message to self such that it will be emitted right after 117ea88969fSopenharmony_ci /// the main message. 118ea88969fSopenharmony_ci pub fn span_error(self, span: Span, msg: String) -> Self { 119ea88969fSopenharmony_ci self.span_range_error( 120ea88969fSopenharmony_ci SpanRange { 121ea88969fSopenharmony_ci first: span, 122ea88969fSopenharmony_ci last: span, 123ea88969fSopenharmony_ci }, 124ea88969fSopenharmony_ci msg, 125ea88969fSopenharmony_ci ) 126ea88969fSopenharmony_ci } 127ea88969fSopenharmony_ci 128ea88969fSopenharmony_ci /// Attach a "help" note to your main message, the note will have it's own span on nightly. 129ea88969fSopenharmony_ci /// 130ea88969fSopenharmony_ci /// # Span 131ea88969fSopenharmony_ci /// 132ea88969fSopenharmony_ci /// The span is ignored on stable, the note effectively inherits its parent's (main message) span 133ea88969fSopenharmony_ci pub fn span_help(self, span: Span, msg: String) -> Self { 134ea88969fSopenharmony_ci self.span_range_help( 135ea88969fSopenharmony_ci SpanRange { 136ea88969fSopenharmony_ci first: span, 137ea88969fSopenharmony_ci last: span, 138ea88969fSopenharmony_ci }, 139ea88969fSopenharmony_ci msg, 140ea88969fSopenharmony_ci ) 141ea88969fSopenharmony_ci } 142ea88969fSopenharmony_ci 143ea88969fSopenharmony_ci /// Attach a "help" note to your main message. 144ea88969fSopenharmony_ci pub fn help(mut self, msg: String) -> Self { 145ea88969fSopenharmony_ci self.suggestions.push((SuggestionKind::Help, msg, None)); 146ea88969fSopenharmony_ci self 147ea88969fSopenharmony_ci } 148ea88969fSopenharmony_ci 149ea88969fSopenharmony_ci /// Attach a note to your main message, the note will have it's own span on nightly. 150ea88969fSopenharmony_ci /// 151ea88969fSopenharmony_ci /// # Span 152ea88969fSopenharmony_ci /// 153ea88969fSopenharmony_ci /// The span is ignored on stable, the note effectively inherits its parent's (main message) span 154ea88969fSopenharmony_ci pub fn span_note(self, span: Span, msg: String) -> Self { 155ea88969fSopenharmony_ci self.span_range_note( 156ea88969fSopenharmony_ci SpanRange { 157ea88969fSopenharmony_ci first: span, 158ea88969fSopenharmony_ci last: span, 159ea88969fSopenharmony_ci }, 160ea88969fSopenharmony_ci msg, 161ea88969fSopenharmony_ci ) 162ea88969fSopenharmony_ci } 163ea88969fSopenharmony_ci 164ea88969fSopenharmony_ci /// Attach a note to your main message 165ea88969fSopenharmony_ci pub fn note(mut self, msg: String) -> Self { 166ea88969fSopenharmony_ci self.suggestions.push((SuggestionKind::Note, msg, None)); 167ea88969fSopenharmony_ci self 168ea88969fSopenharmony_ci } 169ea88969fSopenharmony_ci 170ea88969fSopenharmony_ci /// The message of main warning/error (no notes attached) 171ea88969fSopenharmony_ci pub fn message(&self) -> &str { 172ea88969fSopenharmony_ci &self.msg 173ea88969fSopenharmony_ci } 174ea88969fSopenharmony_ci 175ea88969fSopenharmony_ci /// Abort the proc-macro's execution and display the diagnostic. 176ea88969fSopenharmony_ci /// 177ea88969fSopenharmony_ci /// # Warnings 178ea88969fSopenharmony_ci /// 179ea88969fSopenharmony_ci /// Warnings are not emitted on stable and beta, but this function will abort anyway. 180ea88969fSopenharmony_ci pub fn abort(self) -> ! { 181ea88969fSopenharmony_ci self.emit(); 182ea88969fSopenharmony_ci abort_now() 183ea88969fSopenharmony_ci } 184ea88969fSopenharmony_ci 185ea88969fSopenharmony_ci /// Display the diagnostic while not aborting macro execution. 186ea88969fSopenharmony_ci /// 187ea88969fSopenharmony_ci /// # Warnings 188ea88969fSopenharmony_ci /// 189ea88969fSopenharmony_ci /// Warnings are ignored on stable/beta 190ea88969fSopenharmony_ci pub fn emit(self) { 191ea88969fSopenharmony_ci check_correctness(); 192ea88969fSopenharmony_ci crate::imp::emit_diagnostic(self); 193ea88969fSopenharmony_ci } 194ea88969fSopenharmony_ci} 195ea88969fSopenharmony_ci 196ea88969fSopenharmony_ci/// **NOT PUBLIC API! NOTHING TO SEE HERE!!!** 197ea88969fSopenharmony_ci#[doc(hidden)] 198ea88969fSopenharmony_ciimpl Diagnostic { 199ea88969fSopenharmony_ci pub fn span_suggestion(self, span: Span, suggestion: &str, msg: String) -> Self { 200ea88969fSopenharmony_ci match suggestion { 201ea88969fSopenharmony_ci "help" | "hint" => self.span_help(span, msg), 202ea88969fSopenharmony_ci _ => self.span_note(span, msg), 203ea88969fSopenharmony_ci } 204ea88969fSopenharmony_ci } 205ea88969fSopenharmony_ci 206ea88969fSopenharmony_ci pub fn suggestion(self, suggestion: &str, msg: String) -> Self { 207ea88969fSopenharmony_ci match suggestion { 208ea88969fSopenharmony_ci "help" | "hint" => self.help(msg), 209ea88969fSopenharmony_ci _ => self.note(msg), 210ea88969fSopenharmony_ci } 211ea88969fSopenharmony_ci } 212ea88969fSopenharmony_ci} 213ea88969fSopenharmony_ci 214ea88969fSopenharmony_ciimpl ToTokens for Diagnostic { 215ea88969fSopenharmony_ci fn to_tokens(&self, ts: &mut TokenStream) { 216ea88969fSopenharmony_ci use std::borrow::Cow; 217ea88969fSopenharmony_ci 218ea88969fSopenharmony_ci fn ensure_lf(buf: &mut String, s: &str) { 219ea88969fSopenharmony_ci if s.ends_with('\n') { 220ea88969fSopenharmony_ci buf.push_str(s); 221ea88969fSopenharmony_ci } else { 222ea88969fSopenharmony_ci buf.push_str(s); 223ea88969fSopenharmony_ci buf.push('\n'); 224ea88969fSopenharmony_ci } 225ea88969fSopenharmony_ci } 226ea88969fSopenharmony_ci 227ea88969fSopenharmony_ci fn diag_to_tokens( 228ea88969fSopenharmony_ci span_range: SpanRange, 229ea88969fSopenharmony_ci level: &Level, 230ea88969fSopenharmony_ci msg: &str, 231ea88969fSopenharmony_ci suggestions: &[(SuggestionKind, String, Option<SpanRange>)], 232ea88969fSopenharmony_ci ) -> TokenStream { 233ea88969fSopenharmony_ci if *level == Level::Warning { 234ea88969fSopenharmony_ci return TokenStream::new(); 235ea88969fSopenharmony_ci } 236ea88969fSopenharmony_ci 237ea88969fSopenharmony_ci let message = if suggestions.is_empty() { 238ea88969fSopenharmony_ci Cow::Borrowed(msg) 239ea88969fSopenharmony_ci } else { 240ea88969fSopenharmony_ci let mut message = String::new(); 241ea88969fSopenharmony_ci ensure_lf(&mut message, msg); 242ea88969fSopenharmony_ci message.push('\n'); 243ea88969fSopenharmony_ci 244ea88969fSopenharmony_ci for (kind, note, _span) in suggestions { 245ea88969fSopenharmony_ci message.push_str(" = "); 246ea88969fSopenharmony_ci message.push_str(kind.name()); 247ea88969fSopenharmony_ci message.push_str(": "); 248ea88969fSopenharmony_ci ensure_lf(&mut message, note); 249ea88969fSopenharmony_ci } 250ea88969fSopenharmony_ci message.push('\n'); 251ea88969fSopenharmony_ci 252ea88969fSopenharmony_ci Cow::Owned(message) 253ea88969fSopenharmony_ci }; 254ea88969fSopenharmony_ci 255ea88969fSopenharmony_ci let mut msg = proc_macro2::Literal::string(&message); 256ea88969fSopenharmony_ci msg.set_span(span_range.last); 257ea88969fSopenharmony_ci let group = quote_spanned!(span_range.last=> { #msg } ); 258ea88969fSopenharmony_ci quote_spanned!(span_range.first=> compile_error!#group) 259ea88969fSopenharmony_ci } 260ea88969fSopenharmony_ci 261ea88969fSopenharmony_ci ts.extend(diag_to_tokens( 262ea88969fSopenharmony_ci self.span_range, 263ea88969fSopenharmony_ci &self.level, 264ea88969fSopenharmony_ci &self.msg, 265ea88969fSopenharmony_ci &self.suggestions, 266ea88969fSopenharmony_ci )); 267ea88969fSopenharmony_ci ts.extend( 268ea88969fSopenharmony_ci self.children 269ea88969fSopenharmony_ci .iter() 270ea88969fSopenharmony_ci .map(|(span_range, msg)| diag_to_tokens(*span_range, &Level::Error, &msg, &[])), 271ea88969fSopenharmony_ci ); 272ea88969fSopenharmony_ci } 273ea88969fSopenharmony_ci} 274ea88969fSopenharmony_ci 275ea88969fSopenharmony_ci#[derive(Debug)] 276ea88969fSopenharmony_cipub(crate) enum SuggestionKind { 277ea88969fSopenharmony_ci Help, 278ea88969fSopenharmony_ci Note, 279ea88969fSopenharmony_ci} 280ea88969fSopenharmony_ci 281ea88969fSopenharmony_ciimpl SuggestionKind { 282ea88969fSopenharmony_ci fn name(&self) -> &'static str { 283ea88969fSopenharmony_ci match self { 284ea88969fSopenharmony_ci SuggestionKind::Note => "note", 285ea88969fSopenharmony_ci SuggestionKind::Help => "help", 286ea88969fSopenharmony_ci } 287ea88969fSopenharmony_ci } 288ea88969fSopenharmony_ci} 289ea88969fSopenharmony_ci 290ea88969fSopenharmony_ci#[cfg(feature = "syn-error")] 291ea88969fSopenharmony_ciimpl From<syn::Error> for Diagnostic { 292ea88969fSopenharmony_ci fn from(err: syn::Error) -> Self { 293ea88969fSopenharmony_ci use proc_macro2::{Delimiter, TokenTree}; 294ea88969fSopenharmony_ci 295ea88969fSopenharmony_ci fn gut_error(ts: &mut impl Iterator<Item = TokenTree>) -> Option<(SpanRange, String)> { 296ea88969fSopenharmony_ci let first = match ts.next() { 297ea88969fSopenharmony_ci // compile_error 298ea88969fSopenharmony_ci None => return None, 299ea88969fSopenharmony_ci Some(tt) => tt.span(), 300ea88969fSopenharmony_ci }; 301ea88969fSopenharmony_ci ts.next().unwrap(); // ! 302ea88969fSopenharmony_ci 303ea88969fSopenharmony_ci let lit = match ts.next().unwrap() { 304ea88969fSopenharmony_ci TokenTree::Group(group) => { 305ea88969fSopenharmony_ci // Currently `syn` builds `compile_error!` invocations 306ea88969fSopenharmony_ci // exclusively in `ident{"..."}` (braced) form which is not 307ea88969fSopenharmony_ci // followed by `;` (semicolon). 308ea88969fSopenharmony_ci // 309ea88969fSopenharmony_ci // But if it changes to `ident("...");` (parenthesized) 310ea88969fSopenharmony_ci // or `ident["..."];` (bracketed) form, 311ea88969fSopenharmony_ci // we will need to skip the `;` as well. 312ea88969fSopenharmony_ci // Highly unlikely, but better safe than sorry. 313ea88969fSopenharmony_ci 314ea88969fSopenharmony_ci if group.delimiter() == Delimiter::Parenthesis 315ea88969fSopenharmony_ci || group.delimiter() == Delimiter::Bracket 316ea88969fSopenharmony_ci { 317ea88969fSopenharmony_ci ts.next().unwrap(); // ; 318ea88969fSopenharmony_ci } 319ea88969fSopenharmony_ci 320ea88969fSopenharmony_ci match group.stream().into_iter().next().unwrap() { 321ea88969fSopenharmony_ci TokenTree::Literal(lit) => lit, 322ea88969fSopenharmony_ci _ => unreachable!(), 323ea88969fSopenharmony_ci } 324ea88969fSopenharmony_ci } 325ea88969fSopenharmony_ci _ => unreachable!(), 326ea88969fSopenharmony_ci }; 327ea88969fSopenharmony_ci 328ea88969fSopenharmony_ci let last = lit.span(); 329ea88969fSopenharmony_ci let mut msg = lit.to_string(); 330ea88969fSopenharmony_ci 331ea88969fSopenharmony_ci // "abc" => abc 332ea88969fSopenharmony_ci msg.pop(); 333ea88969fSopenharmony_ci msg.remove(0); 334ea88969fSopenharmony_ci 335ea88969fSopenharmony_ci Some((SpanRange { first, last }, msg)) 336ea88969fSopenharmony_ci } 337ea88969fSopenharmony_ci 338ea88969fSopenharmony_ci let mut ts = err.to_compile_error().into_iter(); 339ea88969fSopenharmony_ci 340ea88969fSopenharmony_ci let (span_range, msg) = gut_error(&mut ts).unwrap(); 341ea88969fSopenharmony_ci let mut res = Diagnostic::spanned_range(span_range, Level::Error, msg); 342ea88969fSopenharmony_ci 343ea88969fSopenharmony_ci while let Some((span_range, msg)) = gut_error(&mut ts) { 344ea88969fSopenharmony_ci res = res.span_range_error(span_range, msg); 345ea88969fSopenharmony_ci } 346ea88969fSopenharmony_ci 347ea88969fSopenharmony_ci res 348ea88969fSopenharmony_ci } 349ea88969fSopenharmony_ci} 350