Skip to content

Commit 36a396c

Browse files
committed
macros: add helper functions for invalid attrs
Remove some duplicated code between both diagnostic derives by introducing helper functions for reporting an error in case of a invalid attribute. Signed-off-by: David Wood <[email protected]>
1 parent 071f072 commit 36a396c

File tree

7 files changed

+225
-291
lines changed

7 files changed

+225
-291
lines changed

compiler/rustc_macros/src/diagnostics/diagnostic.rs

Lines changed: 39 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
#![deny(unused_must_use)]
22

3-
use crate::diagnostics::error::{span_err, throw_span_err, SessionDiagnosticDeriveError};
3+
use crate::diagnostics::error::{
4+
invalid_nested_attr, span_err, throw_invalid_attr, throw_invalid_nested_attr, throw_span_err,
5+
SessionDiagnosticDeriveError,
6+
};
47
use crate::diagnostics::utils::{
58
option_inner_ty, report_error_if_not_applied_to_span, type_matches_path, FieldInfo, HasFieldMap,
69
};
@@ -292,39 +295,24 @@ impl SessionDiagnosticDeriveBuilder {
292295
}
293296

294297
let nested = match meta {
295-
Meta::List(MetaList { nested, .. }) => nested,
296-
Meta::Path(..) => throw_span_err!(
297-
span,
298-
&format!("`#[{}]` is not a valid `SessionDiagnostic` struct attribute", name)
299-
),
300-
Meta::NameValue(..) => throw_span_err!(
301-
span,
302-
&format!("`#[{} = ...]` is not a valid `SessionDiagnostic` struct attribute", name)
303-
),
298+
Meta::List(MetaList { ref nested, .. }) => nested,
299+
_ => throw_invalid_attr!(attr, &meta),
304300
};
305301

306302
let kind = match name {
307303
"error" => SessionDiagnosticKind::Error,
308304
"warning" => SessionDiagnosticKind::Warn,
309-
other => throw_span_err!(
310-
span,
311-
&format!("`#[{}(...)]` is not a valid `SessionDiagnostic` struct attribute", other)
312-
),
305+
_ => throw_invalid_attr!(attr, &meta, |diag| {
306+
diag.help("only `error` and `warning` are valid attributes")
307+
}),
313308
};
314309
self.set_kind_once(kind, span)?;
315310

316311
let mut tokens = Vec::new();
317-
for attr in nested {
318-
let span = attr.span().unwrap();
319-
let meta = match attr {
312+
for nested_attr in nested {
313+
let meta = match nested_attr {
320314
syn::NestedMeta::Meta(meta) => meta,
321-
syn::NestedMeta::Lit(_) => throw_span_err!(
322-
span,
323-
&format!(
324-
"`#[{}(\"...\")]` is not a valid `SessionDiagnostic` struct attribute",
325-
name
326-
)
327-
),
315+
_ => throw_invalid_nested_attr!(attr, &nested_attr),
328316
};
329317

330318
let path = meta.path();
@@ -340,49 +328,12 @@ impl SessionDiagnosticDeriveBuilder {
340328
"code" => {
341329
tokens.push(self.set_code_once(s.value(), s.span().unwrap()));
342330
}
343-
other => {
344-
let diag = span_err(
345-
span,
346-
&format!(
347-
"`#[{}({} = ...)]` is not a valid `SessionDiagnostic` struct attribute",
348-
name, other
349-
),
350-
);
351-
diag.emit();
352-
}
331+
_ => invalid_nested_attr(attr, &nested_attr)
332+
.help("only `slug` and `code` are valid nested attributes")
333+
.emit(),
353334
}
354335
}
355-
Meta::NameValue(..) => {
356-
span_err(
357-
span,
358-
&format!(
359-
"`#[{}({} = ...)]` is not a valid `SessionDiagnostic` struct attribute",
360-
name, nested_name
361-
),
362-
)
363-
.help("value must be a string")
364-
.emit();
365-
}
366-
Meta::Path(..) => {
367-
span_err(
368-
span,
369-
&format!(
370-
"`#[{}({})]` is not a valid `SessionDiagnostic` struct attribute",
371-
name, nested_name
372-
),
373-
)
374-
.emit();
375-
}
376-
Meta::List(..) => {
377-
span_err(
378-
span,
379-
&format!(
380-
"`#[{}({}(...))]` is not a valid `SessionDiagnostic` struct attribute",
381-
name, nested_name
382-
),
383-
)
384-
.emit();
385-
}
336+
_ => invalid_nested_attr(attr, &nested_attr).emit(),
386337
}
387338
}
388339

@@ -478,7 +429,6 @@ impl SessionDiagnosticDeriveBuilder {
478429
info: FieldInfo<'_>,
479430
) -> Result<TokenStream, SessionDiagnosticDeriveError> {
480431
let diag = &self.diag;
481-
let span = attr.span().unwrap();
482432
let field_binding = &info.binding.binding;
483433

484434
let name = attr.path.segments.last().unwrap().ident.to_string();
@@ -502,66 +452,46 @@ impl SessionDiagnosticDeriveBuilder {
502452
report_error_if_not_applied_to_span(attr, &info)?;
503453
Ok(self.add_subdiagnostic(field_binding, name, name))
504454
}
505-
other => throw_span_err!(
506-
span,
507-
&format!("`#[{}]` is not a valid `SessionDiagnostic` field attribute", other)
508-
),
455+
_ => throw_invalid_attr!(attr, &meta, |diag| {
456+
diag
457+
.help("only `skip_arg`, `primary_span`, `label`, `note` and `help` are valid field attributes")
458+
}),
509459
},
510-
Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => match name {
460+
Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(ref s), .. }) => match name {
511461
"label" | "note" | "help" => {
512462
report_error_if_not_applied_to_span(attr, &info)?;
513463
Ok(self.add_subdiagnostic(field_binding, name, &s.value()))
514464
}
515-
other => throw_span_err!(
516-
span,
517-
&format!(
518-
"`#[{} = ...]` is not a valid `SessionDiagnostic` field attribute",
519-
other
520-
)
521-
),
465+
_ => throw_invalid_attr!(attr, &meta, |diag| {
466+
diag.help("only `label`, `note` and `help` are valid field attributes")
467+
}),
522468
},
523-
Meta::NameValue(_) => throw_span_err!(
524-
span,
525-
&format!("`#[{} = ...]` is not a valid `SessionDiagnostic` field attribute", name),
526-
|diag| diag.help("value must be a string")
527-
),
528-
Meta::List(MetaList { path, nested, .. }) => {
469+
Meta::List(MetaList { ref path, ref nested, .. }) => {
529470
let name = path.segments.last().unwrap().ident.to_string();
530471
let name = name.as_ref();
531472

532473
match name {
533474
"suggestion" | "suggestion_short" | "suggestion_hidden"
534475
| "suggestion_verbose" => (),
535-
other => throw_span_err!(
536-
span,
537-
&format!(
538-
"`#[{}(...)]` is not a valid `SessionDiagnostic` field attribute",
539-
other
540-
)
541-
),
476+
_ => throw_invalid_attr!(attr, &meta, |diag| {
477+
diag
478+
.help("only `suggestion{,_short,_hidden,_verbose}` are valid field attributes")
479+
}),
542480
};
543481

544482
let (span_, applicability) = self.span_and_applicability_of_ty(info)?;
545483

546484
let mut msg = None;
547485
let mut code = None;
548486

549-
for attr in nested {
550-
let meta = match attr {
551-
syn::NestedMeta::Meta(meta) => meta,
552-
syn::NestedMeta::Lit(_) => throw_span_err!(
553-
span,
554-
&format!(
555-
"`#[{}(\"...\")]` is not a valid `SessionDiagnostic` field attribute",
556-
name
557-
)
558-
),
487+
for nested_attr in nested {
488+
let meta = match nested_attr {
489+
syn::NestedMeta::Meta(ref meta) => meta,
490+
syn::NestedMeta::Lit(_) => throw_invalid_nested_attr!(attr, &nested_attr),
559491
};
560492

561-
let span = meta.span().unwrap();
562493
let nested_name = meta.path().segments.last().unwrap().ident.to_string();
563494
let nested_name = nested_name.as_str();
564-
565495
match meta {
566496
Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
567497
match nested_name {
@@ -572,37 +502,14 @@ impl SessionDiagnosticDeriveBuilder {
572502
let formatted_str = self.build_format(&s.value(), s.span());
573503
code = Some(formatted_str);
574504
}
575-
other => throw_span_err!(
576-
span,
577-
&format!(
578-
"`#[{}({} = ...)]` is not a valid `SessionDiagnostic` field attribute",
579-
name, other
505+
_ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
506+
diag.help(
507+
"only `message` and `code` are valid field attributes",
580508
)
581-
),
509+
}),
582510
}
583511
}
584-
Meta::NameValue(..) => throw_span_err!(
585-
span,
586-
&format!(
587-
"`#[{}({} = ...)]` is not a valid `SessionDiagnostic` struct attribute",
588-
name, nested_name
589-
),
590-
|diag| diag.help("value must be a string")
591-
),
592-
Meta::Path(..) => throw_span_err!(
593-
span,
594-
&format!(
595-
"`#[{}({})]` is not a valid `SessionDiagnostic` struct attribute",
596-
name, nested_name
597-
)
598-
),
599-
Meta::List(..) => throw_span_err!(
600-
span,
601-
&format!(
602-
"`#[{}({}(...))]` is not a valid `SessionDiagnostic` struct attribute",
603-
name, nested_name
604-
)
605-
),
512+
_ => throw_invalid_nested_attr!(attr, &nested_attr),
606513
}
607514
}
608515

@@ -619,6 +526,7 @@ impl SessionDiagnosticDeriveBuilder {
619526

620527
Ok(quote! { #diag.#method(#span_, #msg, #code, #applicability); })
621528
}
529+
_ => throw_invalid_attr!(attr, &meta),
622530
}
623531
}
624532

Lines changed: 90 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use proc_macro::{Diagnostic, Level, MultiSpan};
22
use proc_macro2::TokenStream;
33
use quote::quote;
4-
use syn;
4+
use syn::{spanned::Spanned, Attribute, Error as SynError, Meta, NestedMeta};
55

66
#[derive(Debug)]
77
pub(crate) enum SessionDiagnosticDeriveError {
8-
SynError(syn::Error),
8+
SynError(SynError),
99
ErrorHandled,
1010
}
1111

@@ -24,37 +24,109 @@ impl SessionDiagnosticDeriveError {
2424
}
2525
}
2626

27+
impl From<SynError> for SessionDiagnosticDeriveError {
28+
fn from(e: SynError) -> Self {
29+
SessionDiagnosticDeriveError::SynError(e)
30+
}
31+
}
32+
33+
/// Helper function for use with `throw_*` macros - constraints `$f` to an `impl FnOnce`.
34+
pub(crate) fn _throw_err(
35+
diag: Diagnostic,
36+
f: impl FnOnce(Diagnostic) -> Diagnostic,
37+
) -> SessionDiagnosticDeriveError {
38+
f(diag).emit();
39+
SessionDiagnosticDeriveError::ErrorHandled
40+
}
41+
42+
/// Returns an error diagnostic on span `span` with msg `msg`.
2743
pub(crate) fn span_err(span: impl MultiSpan, msg: &str) -> Diagnostic {
2844
Diagnostic::spanned(span, Level::Error, msg)
2945
}
3046

31-
/// For methods that return a `Result<_, SessionDiagnosticDeriveError>`:
32-
///
3347
/// Emit a diagnostic on span `$span` with msg `$msg` (optionally performing additional decoration
3448
/// using the `FnOnce` passed in `diag`) and return `Err(ErrorHandled)`.
49+
///
50+
/// For methods that return a `Result<_, SessionDiagnosticDeriveError>`:
3551
macro_rules! throw_span_err {
3652
($span:expr, $msg:expr) => {{ throw_span_err!($span, $msg, |diag| diag) }};
3753
($span:expr, $msg:expr, $f:expr) => {{
38-
return Err(crate::diagnostics::error::_throw_span_err($span, $msg, $f));
54+
let diag = span_err($span, $msg);
55+
return Err(crate::diagnostics::error::_throw_err(diag, $f));
3956
}};
4057
}
4158

4259
pub(crate) use throw_span_err;
4360

44-
/// When possible, prefer using `throw_span_err!` over using this function directly. This only
45-
/// exists as a function to constrain `f` to an `impl FnOnce`.
46-
pub(crate) fn _throw_span_err(
47-
span: impl MultiSpan,
48-
msg: &str,
49-
f: impl FnOnce(Diagnostic) -> Diagnostic,
50-
) -> SessionDiagnosticDeriveError {
51-
let diag = span_err(span, msg);
52-
f(diag).emit();
53-
SessionDiagnosticDeriveError::ErrorHandled
61+
/// Returns an error diagnostic for an invalid attribute.
62+
pub(crate) fn invalid_attr(attr: &Attribute, meta: &Meta) -> Diagnostic {
63+
let span = attr.span().unwrap();
64+
let name = attr.path.segments.last().unwrap().ident.to_string();
65+
let name = name.as_str();
66+
67+
match meta {
68+
Meta::Path(_) => span_err(span, &format!("`#[{}]` is not a valid attribute", name)),
69+
Meta::NameValue(_) => {
70+
span_err(span, &format!("`#[{} = ...]` is not a valid attribute", name))
71+
}
72+
Meta::List(_) => span_err(span, &format!("`#[{}(...)]` is not a valid attribute", name)),
73+
}
5474
}
5575

56-
impl From<syn::Error> for SessionDiagnosticDeriveError {
57-
fn from(e: syn::Error) -> Self {
58-
SessionDiagnosticDeriveError::SynError(e)
76+
/// Emit a error diagnostic for an invalid attribute (optionally performing additional decoration
77+
/// using the `FnOnce` passed in `diag`) and return `Err(ErrorHandled)`.
78+
///
79+
/// For methods that return a `Result<_, SessionDiagnosticDeriveError>`:
80+
macro_rules! throw_invalid_attr {
81+
($attr:expr, $meta:expr) => {{ throw_invalid_attr!($attr, $meta, |diag| diag) }};
82+
($attr:expr, $meta:expr, $f:expr) => {{
83+
let diag = crate::diagnostics::error::invalid_attr($attr, $meta);
84+
return Err(crate::diagnostics::error::_throw_err(diag, $f));
85+
}};
86+
}
87+
88+
pub(crate) use throw_invalid_attr;
89+
90+
/// Returns an error diagnostic for an invalid nested attribute.
91+
pub(crate) fn invalid_nested_attr(attr: &Attribute, nested: &NestedMeta) -> Diagnostic {
92+
let name = attr.path.segments.last().unwrap().ident.to_string();
93+
let name = name.as_str();
94+
95+
let span = nested.span().unwrap();
96+
let meta = match nested {
97+
syn::NestedMeta::Meta(meta) => meta,
98+
syn::NestedMeta::Lit(_) => {
99+
return span_err(span, &format!("`#[{}(\"...\")]` is not a valid attribute", name));
100+
}
101+
};
102+
103+
let span = meta.span().unwrap();
104+
let nested_name = meta.path().segments.last().unwrap().ident.to_string();
105+
let nested_name = nested_name.as_str();
106+
match meta {
107+
Meta::NameValue(..) => span_err(
108+
span,
109+
&format!("`#[{}({} = ...)]` is not a valid attribute", name, nested_name),
110+
),
111+
Meta::Path(..) => {
112+
span_err(span, &format!("`#[{}({})]` is not a valid attribute", name, nested_name))
113+
}
114+
Meta::List(..) => {
115+
span_err(span, &format!("`#[{}({}(...))]` is not a valid attribute", name, nested_name))
116+
}
59117
}
60118
}
119+
120+
/// Emit a error diagnostic for an invalid nested attribute (optionally performing additional
121+
/// decoration using the `FnOnce` passed in `diag`) and return `Err(ErrorHandled)`.
122+
///
123+
/// For methods that return a `Result<_, SessionDiagnosticDeriveError>`:
124+
macro_rules! throw_invalid_nested_attr {
125+
($attr:expr, $nested_attr:expr) => {{ throw_invalid_nested_attr!($attr, $nested_attr, |diag| diag) }};
126+
($attr:expr, $nested_attr:expr, $f:expr) => {{
127+
let diag = crate::diagnostics::error::invalid_nested_attr($attr, $nested_attr);
128+
return Err(crate::diagnostics::error::_throw_err(diag, $f));
129+
}};
130+
}
131+
132+
pub(crate) use throw_invalid_nested_attr;

0 commit comments

Comments
 (0)