1 use crate::diagnostics
::error
::{span_err, throw_span_err, SessionDiagnosticDeriveError}
;
3 use proc_macro2
::TokenStream
;
4 use quote
::{format_ident, quote, ToTokens}
;
5 use std
::collections
::BTreeSet
;
7 use syn
::{spanned::Spanned, Attribute, Meta, Type, TypeTuple}
;
8 use synstructure
::BindingInfo
;
10 /// Checks whether the type name of `ty` matches `name`.
12 /// Given some struct at `a::b::c::Foo`, this will return true for `c::Foo`, `b::c::Foo`, or
13 /// `a::b::c::Foo`. This reasonably allows qualified names to be used in the macro.
14 pub(crate) fn type_matches_path(ty
: &Type
, name
: &[&str]) -> bool
{
15 if let Type
::Path(ty
) = ty
{
19 .map(|s
| s
.ident
.to_string())
21 .zip(name
.iter().rev())
22 .all(|(x
, y
)| &x
.as_str() == y
)
28 /// Checks whether the type `ty` is `()`.
29 pub(crate) fn type_is_unit(ty
: &Type
) -> bool
{
30 if let Type
::Tuple(TypeTuple { elems, .. }
) = ty { elems.is_empty() }
else { false }
33 /// Reports a type error for field with `attr`.
34 pub(crate) fn report_type_error(
37 ) -> Result
<!, SessionDiagnosticDeriveError
> {
38 let name
= attr
.path
.segments
.last().unwrap().ident
.to_string();
39 let meta
= attr
.parse_meta()?
;
44 "the `#[{}{}]` attribute can only be applied to fields of type {}",
48 Meta
::NameValue(_
) => " = ...",
49 Meta
::List(_
) => "(...)",
56 /// Reports an error if the field's type does not match `path`.
57 fn report_error_if_not_applied_to_ty(
62 ) -> Result
<(), SessionDiagnosticDeriveError
> {
63 if !type_matches_path(&info
.ty
, path
) {
64 report_type_error(attr
, ty_name
)?
;
70 /// Reports an error if the field's type is not `Applicability`.
71 pub(crate) fn report_error_if_not_applied_to_applicability(
74 ) -> Result
<(), SessionDiagnosticDeriveError
> {
75 report_error_if_not_applied_to_ty(
78 &["rustc_errors", "Applicability"],
83 /// Reports an error if the field's type is not `Span`.
84 pub(crate) fn report_error_if_not_applied_to_span(
87 ) -> Result
<(), SessionDiagnosticDeriveError
> {
88 report_error_if_not_applied_to_ty(attr
, info
, &["rustc_span", "Span"], "`Span`")
91 /// Inner type of a field and type of wrapper.
92 pub(crate) enum FieldInnerTy
<'ty
> {
93 /// Field is wrapped in a `Option<$inner>`.
95 /// Field is wrapped in a `Vec<$inner>`.
97 /// Field isn't wrapped in an outer type.
101 impl<'ty
> FieldInnerTy
<'ty
> {
102 /// Returns inner type for a field, if there is one.
104 /// - If `ty` is an `Option`, returns `FieldInnerTy::Option { inner: (inner type) }`.
105 /// - If `ty` is a `Vec`, returns `FieldInnerTy::Vec { inner: (inner type) }`.
106 /// - Otherwise returns `None`.
107 pub(crate) fn from_type(ty
: &'ty Type
) -> Self {
108 let variant
: &dyn Fn(&'ty Type
) -> FieldInnerTy
<'ty
> =
109 if type_matches_path(ty
, &["std", "option", "Option"]) {
110 &FieldInnerTy
::Option
111 } else if type_matches_path(ty
, &["std", "vec", "Vec"]) {
114 return FieldInnerTy
::None
;
117 if let Type
::Path(ty_path
) = ty
{
118 let path
= &ty_path
.path
;
119 let ty
= path
.segments
.iter().last().unwrap();
120 if let syn
::PathArguments
::AngleBracketed(bracketed
) = &ty
.arguments
{
121 if bracketed
.args
.len() == 1 {
122 if let syn
::GenericArgument
::Type(ty
) = &bracketed
.args
[0] {
132 /// Returns `Option` containing inner type if there is one.
133 pub(crate) fn inner_type(&self) -> Option
<&'ty Type
> {
135 FieldInnerTy
::Option(inner
) | FieldInnerTy
::Vec(inner
) => Some(inner
),
136 FieldInnerTy
::None
=> None
,
140 /// Surrounds `inner` with destructured wrapper type, exposing inner type as `binding`.
141 pub(crate) fn with(&self, binding
: impl ToTokens
, inner
: impl ToTokens
) -> TokenStream
{
143 FieldInnerTy
::Option(..) => quote
! {
144 if let Some(#binding) = #binding {
148 FieldInnerTy
::Vec(..) => quote
! {
149 for #binding in #binding {
153 FieldInnerTy
::None
=> quote
! { #inner }
,
158 /// Field information passed to the builder. Deliberately omits attrs to discourage the
159 /// `generate_*` methods from walking the attributes themselves.
160 pub(crate) struct FieldInfo
<'a
> {
161 pub(crate) binding
: &'a BindingInfo
<'a
>,
162 pub(crate) ty
: &'a Type
,
163 pub(crate) span
: &'a proc_macro2
::Span
,
166 /// Small helper trait for abstracting over `Option` fields that contain a value and a `Span`
167 /// for error reporting if they are set more than once.
168 pub(crate) trait SetOnce
<T
> {
169 fn set_once(&mut self, value
: T
);
172 impl<T
> SetOnce
<(T
, Span
)> for Option
<(T
, Span
)> {
173 fn set_once(&mut self, (value
, span
): (T
, Span
)) {
176 *self = Some((value
, span
));
178 Some((_
, prev_span
)) => {
179 span_err(span
, "specified multiple times")
180 .span_note(*prev_span
, "previously specified here")
187 pub(crate) trait HasFieldMap
{
188 /// Returns the binding for the field with the given name, if it exists on the type.
189 fn get_field_binding(&self, field
: &String
) -> Option
<&TokenStream
>;
191 /// In the strings in the attributes supplied to this macro, we want callers to be able to
192 /// reference fields in the format string. For example:
194 /// ```ignore (not-usage-example)
195 /// /// Suggest `==` when users wrote `===`.
196 /// #[suggestion(slug = "parser-not-javascript-eq", code = "{lhs} == {rhs}")]
197 /// struct NotJavaScriptEq {
205 /// We want to automatically pick up that `{lhs}` refers `self.lhs` and `{rhs}` refers to
206 /// `self.rhs`, then generate this call to `format!`:
208 /// ```ignore (not-usage-example)
209 /// format!("{lhs} == {rhs}", lhs = self.lhs, rhs = self.rhs)
212 /// This function builds the entire call to `format!`.
213 fn build_format(&self, input
: &str, span
: proc_macro2
::Span
) -> TokenStream
{
214 // This set is used later to generate the final format string. To keep builds reproducible,
215 // the iteration order needs to be deterministic, hence why we use a `BTreeSet` here
216 // instead of a `HashSet`.
217 let mut referenced_fields
: BTreeSet
<String
> = BTreeSet
::new();
219 // At this point, we can start parsing the format string.
220 let mut it
= input
.chars().peekable();
222 // Once the start of a format string has been found, process the format string and spit out
223 // the referenced fields. Leaves `it` sitting on the closing brace of the format string, so
224 // the next call to `it.next()` retrieves the next character.
225 while let Some(c
) = it
.next() {
226 if c
== '
{'
&& *it
.peek().unwrap_or(&'
\0'
) != '
{'
{
227 let mut eat_argument
= || -> Option
<String
> {
228 let mut result
= String
::new();
229 // Format specifiers look like:
231 // format := '{' [ argument ] [ ':' format_spec ] '}' .
233 // Therefore, we only need to eat until ':' or '}' to find the argument.
234 while let Some(c
) = it
.next() {
236 let next
= *it
.peek().unwrap_or(&'
\0'
);
239 } else if next
== '
:'
{
240 // Eat the ':' character.
241 assert_eq
!(it
.next().unwrap(), '
:'
);
245 // Eat until (and including) the matching '}'
246 while it
.next()?
!= '
}'
{
252 if let Some(referenced_field
) = eat_argument() {
253 referenced_fields
.insert(referenced_field
);
258 // At this point, `referenced_fields` contains a set of the unique fields that were
259 // referenced in the format string. Generate the corresponding "x = self.x" format
260 // string parameters:
261 let args
= referenced_fields
.into_iter().map(|field
: String
| {
262 let field_ident
= format_ident
!("{}", field
);
263 let value
= match self.get_field_binding(&field
) {
264 Some(value
) => value
.clone(),
265 // This field doesn't exist. Emit a diagnostic.
269 &format
!("`{}` doesn't refer to a field on this type", field
),
278 #field_ident = #value
282 format
!(#input #(,#args)*)
287 /// `Applicability` of a suggestion - mirrors `rustc_errors::Applicability` - and used to represent
288 /// the user's selection of applicability if specified in an attribute.
289 pub(crate) enum Applicability
{
296 impl FromStr
for Applicability
{
299 fn from_str(s
: &str) -> Result
<Self, Self::Err
> {
301 "machine-applicable" => Ok(Applicability
::MachineApplicable
),
302 "maybe-incorrect" => Ok(Applicability
::MaybeIncorrect
),
303 "has-placeholders" => Ok(Applicability
::HasPlaceholders
),
304 "unspecified" => Ok(Applicability
::Unspecified
),
310 impl quote
::ToTokens
for Applicability
{
311 fn to_tokens(&self, tokens
: &mut TokenStream
) {
312 tokens
.extend(match self {
313 Applicability
::MachineApplicable
=> {
314 quote
! { rustc_errors::Applicability::MachineApplicable }
316 Applicability
::MaybeIncorrect
=> {
317 quote
! { rustc_errors::Applicability::MaybeIncorrect }
319 Applicability
::HasPlaceholders
=> {
320 quote
! { rustc_errors::Applicability::HasPlaceholders }
322 Applicability
::Unspecified
=> {
323 quote
! { rustc_errors::Applicability::Unspecified }