summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_macros/src/diagnostics/utils.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_macros/src/diagnostics/utils.rs')
-rw-r--r--compiler/rustc_macros/src/diagnostics/utils.rs232
1 files changed, 126 insertions, 106 deletions
diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs
index 27b8f676f..b9b09c662 100644
--- a/compiler/rustc_macros/src/diagnostics/utils.rs
+++ b/compiler/rustc_macros/src/diagnostics/utils.rs
@@ -1,5 +1,5 @@
use crate::diagnostics::error::{
- span_err, throw_invalid_attr, throw_invalid_nested_attr, throw_span_err, DiagnosticDeriveError,
+ span_err, throw_invalid_attr, throw_span_err, DiagnosticDeriveError,
};
use proc_macro::Span;
use proc_macro2::{Ident, TokenStream};
@@ -8,11 +8,13 @@ use std::cell::RefCell;
use std::collections::{BTreeSet, HashMap};
use std::fmt;
use std::str::FromStr;
+use syn::meta::ParseNestedMeta;
+use syn::punctuated::Punctuated;
+use syn::{parenthesized, LitStr, Path, Token};
use syn::{spanned::Spanned, Attribute, Field, Meta, Type, TypeTuple};
-use syn::{MetaList, MetaNameValue, NestedMeta, Path};
use synstructure::{BindingInfo, VariantInfo};
-use super::error::{invalid_attr, invalid_nested_attr};
+use super::error::invalid_attr;
thread_local! {
pub static CODE_IDENT_COUNT: RefCell<u32> = RefCell::new(0);
@@ -50,13 +52,18 @@ pub(crate) fn type_is_unit(ty: &Type) -> bool {
if let Type::Tuple(TypeTuple { elems, .. }) = ty { elems.is_empty() } else { false }
}
+/// Checks whether the type `ty` is `bool`.
+pub(crate) fn type_is_bool(ty: &Type) -> bool {
+ type_matches_path(ty, &["bool"])
+}
+
/// Reports a type error for field with `attr`.
pub(crate) fn report_type_error(
attr: &Attribute,
ty_name: &str,
) -> Result<!, DiagnosticDeriveError> {
- let name = attr.path.segments.last().unwrap().ident.to_string();
- let meta = attr.parse_meta()?;
+ let name = attr.path().segments.last().unwrap().ident.to_string();
+ let meta = &attr.meta;
throw_span_err!(
attr.span().unwrap(),
@@ -192,6 +199,11 @@ impl<'ty> FieldInnerTy<'ty> {
#inner
}
},
+ FieldInnerTy::Plain(t) if type_is_bool(t) => quote! {
+ if #binding {
+ #inner
+ }
+ },
FieldInnerTy::Plain(..) => quote! { #inner },
}
}
@@ -408,59 +420,62 @@ pub(super) enum AllowMultipleAlternatives {
Yes,
}
+fn parse_suggestion_values(
+ nested: ParseNestedMeta<'_>,
+ allow_multiple: AllowMultipleAlternatives,
+) -> syn::Result<Vec<LitStr>> {
+ let values = if let Ok(val) = nested.value() {
+ vec![val.parse()?]
+ } else {
+ let content;
+ parenthesized!(content in nested.input);
+
+ if let AllowMultipleAlternatives::No = allow_multiple {
+ span_err(
+ nested.input.span().unwrap(),
+ "expected exactly one string literal for `code = ...`",
+ )
+ .emit();
+ vec![]
+ } else {
+ let literals = Punctuated::<LitStr, Token![,]>::parse_terminated(&content);
+
+ match literals {
+ Ok(p) if p.is_empty() => {
+ span_err(
+ content.span().unwrap(),
+ "expected at least one string literal for `code(...)`",
+ )
+ .emit();
+ vec![]
+ }
+ Ok(p) => p.into_iter().collect(),
+ Err(_) => {
+ span_err(
+ content.span().unwrap(),
+ "`code(...)` must contain only string literals",
+ )
+ .emit();
+ vec![]
+ }
+ }
+ }
+ };
+
+ Ok(values)
+}
+
/// Constructs the `format!()` invocation(s) necessary for a `#[suggestion*(code = "foo")]` or
/// `#[suggestion*(code("foo", "bar"))]` attribute field
pub(super) fn build_suggestion_code(
code_field: &Ident,
- meta: &Meta,
+ nested: ParseNestedMeta<'_>,
fields: &impl HasFieldMap,
allow_multiple: AllowMultipleAlternatives,
) -> TokenStream {
- let values = match meta {
- // `code = "foo"`
- Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => vec![s],
- // `code("foo", "bar")`
- Meta::List(MetaList { nested, .. }) => {
- if let AllowMultipleAlternatives::No = allow_multiple {
- span_err(
- meta.span().unwrap(),
- "expected exactly one string literal for `code = ...`",
- )
- .emit();
- vec![]
- } else if nested.is_empty() {
- span_err(
- meta.span().unwrap(),
- "expected at least one string literal for `code(...)`",
- )
- .emit();
- vec![]
- } else {
- nested
- .into_iter()
- .filter_map(|item| {
- if let NestedMeta::Lit(syn::Lit::Str(s)) = item {
- Some(s)
- } else {
- span_err(
- item.span().unwrap(),
- "`code(...)` must contain only string literals",
- )
- .emit();
- None
- }
- })
- .collect()
- }
- }
- _ => {
- span_err(
- meta.span().unwrap(),
- r#"`code = "..."`/`code(...)` must contain only string literals"#,
- )
- .emit();
- vec![]
- }
+ let values = match parse_suggestion_values(nested, allow_multiple) {
+ Ok(x) => x,
+ Err(e) => return e.into_compile_error(),
};
if let AllowMultipleAlternatives::Yes = allow_multiple {
@@ -591,11 +606,9 @@ impl SubdiagnosticKind {
let span = attr.span().unwrap();
- let name = attr.path.segments.last().unwrap().ident.to_string();
+ let name = attr.path().segments.last().unwrap().ident.to_string();
let name = name.as_str();
- let meta = attr.parse_meta()?;
-
let mut kind = match name {
"label" => SubdiagnosticKind::Label,
"note" => SubdiagnosticKind::Note,
@@ -608,7 +621,7 @@ impl SubdiagnosticKind {
name.strip_prefix("suggestion").and_then(SuggestionKind::from_suffix)
{
if suggestion_kind != SuggestionKind::Normal {
- invalid_attr(attr, &meta)
+ invalid_attr(attr)
.help(format!(
r#"Use `#[suggestion(..., style = "{suggestion_kind}")]` instead"#
))
@@ -625,7 +638,7 @@ impl SubdiagnosticKind {
name.strip_prefix("multipart_suggestion").and_then(SuggestionKind::from_suffix)
{
if suggestion_kind != SuggestionKind::Normal {
- invalid_attr(attr, &meta)
+ invalid_attr(attr)
.help(format!(
r#"Use `#[multipart_suggestion(..., style = "{suggestion_kind}")]` instead"#
))
@@ -637,16 +650,16 @@ impl SubdiagnosticKind {
applicability: None,
}
} else {
- throw_invalid_attr!(attr, &meta);
+ throw_invalid_attr!(attr);
}
}
};
- let nested = match meta {
- Meta::List(MetaList { ref nested, .. }) => {
+ let list = match &attr.meta {
+ Meta::List(list) => {
// An attribute with properties, such as `#[suggestion(code = "...")]` or
// `#[error(some::slug)]`
- nested
+ list
}
Meta::Path(_) => {
// An attribute without a slug or other properties, such as `#[note]` - return
@@ -668,69 +681,68 @@ impl SubdiagnosticKind {
}
}
_ => {
- throw_invalid_attr!(attr, &meta)
+ throw_invalid_attr!(attr)
}
};
let mut code = None;
let mut suggestion_kind = None;
- let mut nested_iter = nested.into_iter().peekable();
+ let mut first = true;
+ let mut slug = None;
- // Peek at the first nested attribute: if it's a slug path, consume it.
- let slug = if let Some(NestedMeta::Meta(Meta::Path(path))) = nested_iter.peek() {
- let path = path.clone();
- // Advance the iterator.
- nested_iter.next();
- Some(path)
- } else {
- None
- };
-
- for nested_attr in nested_iter {
- let meta = match nested_attr {
- NestedMeta::Meta(ref meta) => meta,
- NestedMeta::Lit(_) => {
- invalid_nested_attr(attr, nested_attr).emit();
- continue;
+ list.parse_nested_meta(|nested| {
+ if nested.input.is_empty() || nested.input.peek(Token![,]) {
+ if first {
+ slug = Some(nested.path);
+ } else {
+ span_err(nested.input.span().unwrap(), "a diagnostic slug must be the first argument to the attribute").emit();
}
- };
- let span = meta.span().unwrap();
- let nested_name = meta.path().segments.last().unwrap().ident.to_string();
+ first = false;
+ return Ok(());
+ }
+
+ first = false;
+
+ let nested_name = nested.path.segments.last().unwrap().ident.to_string();
let nested_name = nested_name.as_str();
- let string_value = match meta {
- Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(value), .. }) => Some(value),
+ let path_span = nested.path.span().unwrap();
+ let val_span = nested.input.span().unwrap();
- Meta::Path(_) => throw_invalid_nested_attr!(attr, nested_attr, |diag| {
- diag.help("a diagnostic slug must be the first argument to the attribute")
- }),
- _ => None,
- };
+ macro_rules! get_string {
+ () => {{
+ let Ok(value) = nested.value().and_then(|x| x.parse::<LitStr>()) else {
+ span_err(val_span, "expected `= \"xxx\"`").emit();
+ return Ok(());
+ };
+ value
+ }};
+ }
+
+ let mut has_errors = false;
+ let input = nested.input;
match (nested_name, &mut kind) {
("code", SubdiagnosticKind::Suggestion { code_field, .. }) => {
let code_init = build_suggestion_code(
code_field,
- meta,
+ nested,
fields,
AllowMultipleAlternatives::Yes,
);
- code.set_once(code_init, span);
+ code.set_once(code_init, path_span);
}
(
"applicability",
SubdiagnosticKind::Suggestion { ref mut applicability, .. }
| SubdiagnosticKind::MultipartSuggestion { ref mut applicability, .. },
) => {
- let Some(value) = string_value else {
- invalid_nested_attr(attr, nested_attr).emit();
- continue;
- };
-
+ let value = get_string!();
let value = Applicability::from_str(&value.value()).unwrap_or_else(|()| {
- span_err(span, "invalid applicability").emit();
+ span_err(value.span().unwrap(), "invalid applicability").emit();
+ has_errors = true;
Applicability::Unspecified
});
applicability.set_once(value, span);
@@ -740,15 +752,13 @@ impl SubdiagnosticKind {
SubdiagnosticKind::Suggestion { .. }
| SubdiagnosticKind::MultipartSuggestion { .. },
) => {
- let Some(value) = string_value else {
- invalid_nested_attr(attr, nested_attr).emit();
- continue;
- };
+ let value = get_string!();
let value = value.value().parse().unwrap_or_else(|()| {
span_err(value.span().unwrap(), "invalid suggestion style")
.help("valid styles are `normal`, `short`, `hidden`, `verbose` and `tool-only`")
.emit();
+ has_errors = true;
SuggestionKind::Normal
});
@@ -757,22 +767,32 @@ impl SubdiagnosticKind {
// Invalid nested attribute
(_, SubdiagnosticKind::Suggestion { .. }) => {
- invalid_nested_attr(attr, nested_attr)
+ span_err(path_span, "invalid nested attribute")
.help(
"only `style`, `code` and `applicability` are valid nested attributes",
)
.emit();
+ has_errors = true;
}
(_, SubdiagnosticKind::MultipartSuggestion { .. }) => {
- invalid_nested_attr(attr, nested_attr)
+ span_err(path_span, "invalid nested attribute")
.help("only `style` and `applicability` are valid nested attributes")
- .emit()
+ .emit();
+ has_errors = true;
}
_ => {
- invalid_nested_attr(attr, nested_attr).emit();
+ span_err(path_span, "invalid nested attribute").emit();
+ has_errors = true;
}
}
- }
+
+ if has_errors {
+ // Consume the rest of the input to avoid spamming errors
+ let _ = input.parse::<TokenStream>();
+ }
+
+ Ok(())
+ })?;
match kind {
SubdiagnosticKind::Suggestion {
@@ -835,5 +855,5 @@ pub(super) fn should_generate_set_arg(field: &Field) -> bool {
}
pub(super) fn is_doc_comment(attr: &Attribute) -> bool {
- attr.path.segments.last().unwrap().ident == "doc"
+ attr.path().segments.last().unwrap().ident == "doc"
}