Skip to main content

auto_default/
lib.rs

1#![doc = concat!("[![crates.io](https://img.shields.io/crates/v/", env!("CARGO_PKG_NAME"), "?style=flat-square&logo=rust)](https://crates.io/crates/", env!("CARGO_PKG_NAME"), ")")]
2#![doc = concat!("[![docs.rs](https://img.shields.io/docsrs/", env!("CARGO_PKG_NAME"), "?style=flat-square&logo=docs.rs)](https://docs.rs/", env!("CARGO_PKG_NAME"), ")")]
3#![doc = "![license](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue?style=flat-square)"]
4//! ![msrv](https://img.shields.io/badge/msrv-nightly-blue?style=flat-square&logo=rust)
5#![doc = concat!("[![github](https://img.shields.io/github/stars/nik-rev/", env!("CARGO_PKG_NAME"), ")](https://github.com/nik-rev/", env!("CARGO_PKG_NAME"), ")")]
6//!
7//! This crate provides an attribute macro `#[auto_default]`, which adds a default field value of
8//! `Default::default()` to fields that do not have one.
9//!
10//! ```toml
11#![doc = concat!(env!("CARGO_PKG_NAME"), " = ", "\"", env!("CARGO_PKG_VERSION_MAJOR"), ".", env!("CARGO_PKG_VERSION_MINOR"), "\"")]
12//! ```
13//!
14//! Note: `auto-default` has *zero* dependencies. Not even `syn`! The compile times are very fast.
15//!
16//! # Showcase
17//!
18//! Rust's [default field values](https://github.com/rust-lang/rust/issues/132162) allow
19//! the shorthand `Struct { field, .. }` instead of the lengthy `Struct { field, ..Default::default() }`
20//!
21//! For `..` instead of `..Default::default()` to work,
22//! your `Struct` needs **all** fields to have a default value.
23//!
24//! This often means `= Default::default()` boilerplate on every field, because it is
25//! very common to want field defaults to be the value of their `Default` implementation
26//!
27//! ## Before
28//!
29//! ```rust
30//! # #![feature(default_field_values)]
31//! # #![feature(const_trait_impl)]
32//! # #![feature(const_default)]
33//! # #![feature(derive_const)]
34//! # use auto_default::auto_default;
35//! # #[derive_const(Default)]
36//! # struct Rect { value: f32 }
37//! # #[derive_const(Default)]
38//! # struct Size { value: f32 }
39//! # #[derive_const(Default)]
40//! # struct Point { value: f32 }
41//! #[derive(Default)]
42//! pub struct Layout {
43//!     order: u32 = Default::default(),
44//!     location: Point = Default::default(),
45//!     size: Size = Default::default(),
46//!     content_size: Size = Default::default(),
47//!     scrollbar_size: Size = Default::default(),
48//!     border: Rect = Default::default(),
49//!     padding: Rect = Default::default(),
50//!     margin: Rect = Default::default(),
51//! }
52//! ```
53//!
54//! ## With `#[auto_default]`
55//!
56//! ```rust
57//! # #![feature(default_field_values)]
58//! # #![feature(const_trait_impl)]
59//! # #![feature(const_default)]
60//! # #![feature(derive_const)]
61//! # use auto_default::auto_default;
62//! # #[derive_const(Default)]
63//! # struct Rect { value: f32 }
64//! # #[derive_const(Default)]
65//! # struct Size { value: f32 }
66//! # #[derive_const(Default)]
67//! # struct Point { value: f32 }
68//! #[auto_default]
69//! #[derive(Default)]
70//! pub struct Layout {
71//!     order: u32,
72//!     location: Point,
73//!     size: Size,
74//!     content_size: Size,
75//!     scrollbar_size: Size,
76//!     border: Rect,
77//!     padding: Rect,
78//!     margin: Rect,
79//! }
80//! ```
81//!
82//! You can apply the [`#[auto_default]`](macro@auto_default) macro to `struct`s with named fields, and `enum`s.
83//!
84//! If any field or variant has the `#[auto_default(skip)]` attribute, a default field value of `Default::default()`
85//! will **not** be added
86use std::iter::Peekable;
87
88use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
89
90/// Adds a default field value of `Default::default()` to fields that don't have one
91///
92/// # Example
93///
94/// Turns this:
95///
96/// ```rust
97/// # #![feature(default_field_values)]
98/// # #![feature(const_trait_impl)]
99/// # #![feature(const_default)]
100/// #[auto_default]
101/// struct User {
102///     age: u8,
103///     is_admin: bool = false
104/// }
105/// # use auto_default::auto_default;
106/// ```
107///
108/// Into this:
109///
110/// ```rust
111/// # #![feature(default_field_values)]
112/// # #![feature(const_trait_impl)]
113/// # #![feature(const_default)]
114/// struct User {
115///     age: u8 = Default::default(),
116///     is_admin: bool = false
117/// }
118/// ```
119///
120/// This macro applies to `struct`s with named fields, and enums.
121///
122/// # Do not add `= Default::default()` field value to select fields
123///
124/// If you do not want a specific field to have a default, you can opt-out
125/// with `#[auto_default(skip)]`:
126///
127/// ```rust
128/// # #![feature(default_field_values)]
129/// # #![feature(const_trait_impl)]
130/// # #![feature(const_default)]
131/// #[auto_default]
132/// struct User {
133///     #[auto_default(skip)]
134///     age: u8,
135///     is_admin: bool
136/// }
137/// # use auto_default::auto_default;
138/// ```
139///
140/// The above is transformed into this:
141///
142/// ```rust
143/// # #![feature(default_field_values)]
144/// # #![feature(const_trait_impl)]
145/// # #![feature(const_default)]
146/// struct User {
147///     age: u8,
148///     is_admin: bool = Default::default()
149/// }
150/// ```
151#[proc_macro_attribute]
152pub fn auto_default(args: TokenStream, input: TokenStream) -> TokenStream {
153    let mut compile_errors = TokenStream::new();
154
155    if !args.is_empty() {
156        compile_errors.extend(create_compile_error!(
157            args.into_iter().next(),
158            "no arguments expected",
159        ));
160    }
161
162    // Input supplied by the user. All tokens from here will
163    // get sent back to `output`
164    let mut source = input.into_iter().peekable();
165
166    // We collect all tokens into here and then return this
167    let mut sink = TokenStream::new();
168
169    stream_attrs(
170        &mut source,
171        &mut sink,
172        &mut compile_errors,
173        // no skip allowed on the container, would make no sense
174        // (just don't use the `#[auto_default]` at all at that point!)
175        IsSkipAllowed(false),
176    );
177    stream_vis(&mut source, &mut sink);
178
179    // pub(in crate) struct Foo
180    //               ^^^^^^
181    let item_kind = match source.next() {
182        Some(TokenTree::Ident(kw)) if kw.to_string() == "struct" => {
183            sink.extend([kw]);
184            ItemKind::Struct
185        }
186        Some(TokenTree::Ident(kw)) if kw.to_string() == "enum" => {
187            sink.extend([kw]);
188            ItemKind::Enum
189        }
190        tt => {
191            compile_errors.extend(create_compile_error!(
192                tt,
193                "expected a `struct` or an `enum`"
194            ));
195            return compile_errors;
196        }
197    };
198
199    // struct Foo
200    //        ^^^
201    let item_ident_span = stream_ident(&mut source, &mut sink)
202        .expect("`struct` or `enum` keyword is always followed by an identifier");
203
204    // Generics
205    //
206    // struct Foo<Bar, Baz: Trait> where Baz: Quux { ... }
207    //           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
208    let source_item_fields = loop {
209        match source.next() {
210            // Fields of the struct
211            Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::Brace => break group,
212            // This token is part of the generics of the struct
213            Some(tt) => sink.extend([tt]),
214            // reached end of input
215            None => {
216                // note: if enum, this is unreachable because `enum Foo` is invalid (requires `{}`),
217                // whilst `struct Foo;` is completely valid
218                compile_errors.extend(CompileError::new(
219                    item_ident_span,
220                    "expected struct with named fields",
221                ));
222                return compile_errors;
223            }
224        }
225    };
226
227    match item_kind {
228        ItemKind::Struct => {
229            sink.extend([add_default_field_values(
230                source_item_fields,
231                &mut compile_errors,
232                // none of the fields are considered to be skipped initially
233                IsSkip(false),
234            )]);
235        }
236        ItemKind::Enum => {
237            let mut source_variants = source_item_fields.stream().into_iter().peekable();
238            let mut sink_variants = TokenStream::new();
239
240            loop {
241                // if this variant is marked #[auto_default(skip)]
242                let is_skip = stream_attrs(
243                    &mut source_variants,
244                    &mut sink_variants,
245                    &mut compile_errors,
246                    // can skip the variant, which removes auto-default for all
247                    // fields
248                    IsSkipAllowed(true),
249                );
250
251                // variants technically can have visibility, at least on a syntactic level
252                //
253                // pub Variant {  }
254                // ^^^
255                stream_vis(&mut source_variants, &mut sink_variants);
256
257                // Variant {  }
258                // ^^^^^^^
259                let Some(variant_ident_span) =
260                    stream_ident(&mut source_variants, &mut sink_variants)
261                else {
262                    // that means we have an enum with no variants, e.g.:
263                    //
264                    // enum Never {}
265                    //
266                    // When we parse the variants, there won't be an identifier
267                    break;
268                };
269
270                // only variants with named fields can be marked `#[auto_default(skip)]`
271                let mut disallow_skip = || {
272                    if is_skip.0 {
273                        compile_errors.extend(CompileError::new(
274                            variant_ident_span,
275                            concat!(
276                                "`#[auto_default(skip)]` is",
277                                " only allowed on variants with named fields"
278                            ),
279                        ));
280                    }
281                };
282
283                match source_variants.peek() {
284                    // Enum variant with named fields. Add default field values.
285                    Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::Brace => {
286                        let Some(TokenTree::Group(named_variant_fields)) = source_variants.next()
287                        else {
288                            unreachable!()
289                        };
290                        sink_variants.extend([add_default_field_values(
291                            named_variant_fields,
292                            &mut compile_errors,
293                            is_skip,
294                        )]);
295
296                        stream_enum_variant_discriminant_and_comma(
297                            &mut source_variants,
298                            &mut sink_variants,
299                        );
300                    }
301                    // Enum variant with unnamed fields.
302                    Some(TokenTree::Group(group))
303                        if group.delimiter() == Delimiter::Parenthesis =>
304                    {
305                        disallow_skip();
306                        let Some(TokenTree::Group(unnamed_variant_fields)) = source_variants.next()
307                        else {
308                            unreachable!()
309                        };
310                        sink_variants.extend([unnamed_variant_fields]);
311
312                        stream_enum_variant_discriminant_and_comma(
313                            &mut source_variants,
314                            &mut sink_variants,
315                        );
316                    }
317                    // This was a unit variant. Next variant may exist,
318                    // if it does it is parsed on next iteration
319                    Some(TokenTree::Punct(punct))
320                        if punct.as_char() == ',' || punct.as_char() == '=' =>
321                    {
322                        disallow_skip();
323                        stream_enum_variant_discriminant_and_comma(
324                            &mut source_variants,
325                            &mut sink_variants,
326                        );
327                    }
328                    // Unit variant, with no comma at the end. This is the last variant
329                    None => {
330                        disallow_skip();
331                        break;
332                    }
333                    Some(_) => unreachable!(),
334                }
335            }
336
337            let mut sink_variants = Group::new(source_item_fields.delimiter(), sink_variants);
338            sink_variants.set_span(source_item_fields.span());
339            sink.extend([sink_variants]);
340        }
341    }
342
343    sink.extend(compile_errors);
344
345    sink
346}
347
348struct IsSkip(bool);
349struct IsSkipAllowed(bool);
350
351/// Streams enum variant discriminant + comma at the end from `source` into `sink`
352///
353/// enum Example {
354///     Three,
355///          ^
356///     Two(u32) = 2,
357///             ^^^^^
358///     Four { hello: u32 } = 4,
359///                        ^^^^^
360/// }
361fn stream_enum_variant_discriminant_and_comma(source: &mut Source, sink: &mut Sink) {
362    match source.next() {
363        // No discriminant, there may be another variant after this
364        Some(TokenTree::Punct(punct)) if punct.as_char() == ',' => {
365            sink.extend([punct]);
366        }
367        // No discriminant, this is the final enum variant
368        None => {}
369        // Enum variant has a discriminant
370        Some(TokenTree::Punct(punct)) if punct.as_char() == '=' => {
371            sink.extend([punct]);
372
373            // Stream discriminant expression from `source` into `sink`
374            loop {
375                match source.next() {
376                    // End of discriminant, there may be a variant after this
377                    Some(TokenTree::Punct(punct)) if punct.as_char() == ',' => {
378                        sink.extend([punct]);
379                        break;
380                    }
381                    // This token is part of the variant's expression
382                    Some(tt) => {
383                        sink.extend([tt]);
384                    }
385                    // End of discriminant, this is the last variant
386                    None => break,
387                }
388            }
389        }
390        Some(_) => unreachable!(),
391    }
392}
393
394type Source = Peekable<proc_macro::token_stream::IntoIter>;
395type Sink = TokenStream;
396
397/// Streams the identifier from `input` into `output`, returning its span, if the identifier exists
398fn stream_ident(source: &mut Source, sink: &mut Sink) -> Option<Span> {
399    let ident = source.next()?;
400    let span = ident.span();
401    sink.extend([ident]);
402    Some(span)
403}
404
405// Parses attributes
406//
407// #[attr] #[attr] pub field: Type
408// #[attr] #[attr] struct Foo
409// #[attr] #[attr] enum Foo
410//
411// Returns `true` if `#[auto_default(skip)]` was encountered
412fn stream_attrs(
413    source: &mut Source,
414    sink: &mut Sink,
415    errors: &mut TokenStream,
416    is_skip_allowed: IsSkipAllowed,
417) -> IsSkip {
418    let mut is_skip = None;
419
420    let is_skip = loop {
421        if !matches!(source.peek(), Some(TokenTree::Punct(hash)) if *hash == '#') {
422            break is_skip;
423        };
424
425        // #[some_attr]
426        // ^
427        let pound = source.next();
428
429        // #[some_attr]
430        //  ^^^^^^^^^^^
431        let Some(TokenTree::Group(attr)) = source.next() else {
432            unreachable!()
433        };
434
435        // #[some_attr = hello]
436        //   ^^^^^^^^^^^^^^^^^
437        let mut attr_tokens = attr.stream().into_iter().peekable();
438
439        // Check if this attribute is `#[auto_default(skip)]`
440        if let Some(skip_span) = is_skip_attribute(&mut attr_tokens, errors) {
441            if is_skip.is_some() {
442                // Disallow 2 attributes on a single field:
443                //
444                // #[auto_default(skip)]
445                // #[auto_default(skip)]
446                errors.extend(CompileError::new(
447                    skip_span,
448                    "duplicate `#[auto_default(skip)]`",
449                ));
450            } else {
451                is_skip = Some(skip_span);
452            }
453            continue;
454        }
455
456        // #[attr]
457        // ^
458        sink.extend(pound);
459
460        // Re-construct the `[..]` for the attribute
461        //
462        // #[attr]
463        //  ^^^^^^
464        let mut group = Group::new(attr.delimiter(), attr_tokens.collect());
465        group.set_span(attr.span());
466
467        // #[attr]
468        //  ^^^^^^
469        sink.extend([group]);
470    };
471
472    if let Some(skip_span) = is_skip
473        && !is_skip_allowed.0
474    {
475        errors.extend(CompileError::new(
476            skip_span,
477            "`#[auto_default(skip)]` is not allowed on container",
478        ));
479    }
480
481    IsSkip(is_skip.is_some())
482}
483
484/// if `source` is exactly `auto_default(skip)`, returns `Some(span)`
485/// with `span` being the `Span` of the `skip` identifier
486fn is_skip_attribute(source: &mut Source, errors: &mut TokenStream) -> Option<Span> {
487    let Some(TokenTree::Ident(ident)) = source.peek() else {
488        return None;
489    };
490
491    if ident.to_string() != "auto_default" {
492        return None;
493    };
494
495    // #[auto_default(skip)]
496    //   ^^^^^^^^^^^^
497    let ident = source.next().unwrap();
498
499    // We know it is `#[auto_default ???]`, we need to validate that `???`
500    // is exactly `(skip)` now
501
502    // #[auto_default(skip)]
503    //   ^^^^^^^^^^^^
504    let auto_default_span = ident.span();
505
506    // #[auto_default(skip)]
507    //               ^^^^^^
508    let group = match source.next() {
509        Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::Parenthesis => group,
510        Some(tt) => {
511            errors.extend(CompileError::new(tt.span(), "expected `(skip)`"));
512            return None;
513        }
514        None => {
515            errors.extend(CompileError::new(
516                auto_default_span,
517                "expected `(skip)` after this",
518            ));
519            return None;
520        }
521    };
522
523    // #[auto_default(skip)]
524    //                ^^^^
525    let mut inside = group.stream().into_iter();
526
527    // #[auto_default(skip)]
528    //                ^^^^
529    let ident_skip = match inside.next() {
530        Some(TokenTree::Ident(ident)) => ident,
531        Some(tt) => {
532            errors.extend(CompileError::new(tt.span(), "expected `skip`"));
533            return None;
534        }
535        None => {
536            errors.extend(CompileError::new(
537                group.span(),
538                "expected `(skip)`, found `()`",
539            ));
540            return None;
541        }
542    };
543
544    if ident_skip.to_string() != "skip" {
545        errors.extend(CompileError::new(ident_skip.span(), "expected `skip`"));
546        return None;
547    }
548
549    // Validate that there's nothing after `skip`
550    //
551    // #[auto_default(skip    )]
552    //                    ^^^^
553    if let Some(tt) = inside.next() {
554        errors.extend(CompileError::new(tt.span(), "unexpected token"));
555        return None;
556    }
557
558    Some(ident_skip.span())
559}
560
561fn stream_vis(source: &mut Source, sink: &mut Sink) {
562    // Remove visibility if it is present
563    //
564    // pub(in crate) struct
565    // ^^^^^^^^^^^^^
566    if let Some(TokenTree::Ident(vis)) = source.peek()
567        && vis.to_string() == "pub"
568    {
569        // pub(in crate) struct
570        // ^^^
571        sink.extend(source.next());
572
573        if let Some(TokenTree::Group(group)) = source.peek()
574            && let Delimiter::Parenthesis = group.delimiter()
575        {
576            // pub(in crate) struct
577            //    ^^^^^^^^^^
578            sink.extend(source.next());
579        }
580    };
581}
582
583#[derive(PartialEq)]
584enum ItemKind {
585    Struct,
586    Enum,
587}
588
589/// `fields` is [`StructFields`] in the grammar.
590///
591/// It is the curly braces, and everything within, for a struct with named fields,
592/// or an enum variant with named fields.
593///
594/// These fields are transformed by adding `= Default::default()` to every
595/// field that doesn't already have a default value.
596///
597/// If a field is marked with `#[auto_default(skip)]`, no default value will be
598/// added
599///
600/// [`StructFields`]: https://doc.rust-lang.org/reference/items/structs.html#grammar-StructFields
601fn add_default_field_values(
602    fields: Group,
603    compile_errors: &mut TokenStream,
604    is_skip_variant: IsSkip,
605) -> Group {
606    // All the tokens corresponding to the struct's field, passed by the user
607    // These tokens will eventually all be sent to `output_fields`,
608    // plus a few extra for any `Default::default()` that we output
609    let mut input_fields = fields.stream().into_iter().peekable();
610
611    // The tokens corresponding to the fields of the output struct
612    let mut output_fields = TokenStream::new();
613
614    // Parses all fields.
615    // Each iteration parses a single field
616    'parse_field: loop {
617        let is_skip_field = stream_attrs(
618            &mut input_fields,
619            &mut output_fields,
620            compile_errors,
621            IsSkipAllowed(true),
622        );
623        let is_skip = is_skip_field.0 || is_skip_variant.0;
624        stream_vis(&mut input_fields, &mut output_fields);
625        let Some(field_ident_span) = stream_ident(&mut input_fields, &mut output_fields) else {
626            // No fields. e.g.: `struct Struct {}`
627            break;
628        };
629
630        // field: Type
631        //      ^
632        output_fields.extend(input_fields.next());
633
634        // Everything after the `:` in the field
635        //
636        // Involves:
637        //
638        // - Adding default value of `= Default::default()` if one is not present
639        // - Continue to next iteration of the loop
640        loop {
641            match input_fields.peek() {
642                // This field has a custom default field value
643                //
644                // field: Type = default
645                //             ^
646                Some(TokenTree::Punct(p)) if p.as_char() == '=' => {
647                    if is_skip {
648                        compile_errors.extend(CompileError::new(
649                            field_ident_span,
650                            concat!(
651                                "this field is marked `#[auto_default(skip)]`,",
652                                " which does nothing since this field has a",
653                                " default value: `= ...`\n",
654                                "the attribute `#[auto_default(skip)]` can be removed"
655                            ),
656                        ));
657                    }
658
659                    loop {
660                        match input_fields.next() {
661                            Some(TokenTree::Punct(p)) if p == ',' => {
662                                output_fields.extend([p]);
663                                // Comma after field. Field is finished.
664                                continue 'parse_field;
665                            }
666                            Some(tt) => output_fields.extend([tt]),
667                            // End of input. Field is finished. This is the last field
668                            None => break 'parse_field,
669                        }
670                    }
671                }
672                // Reached end of field, has comma at the end, no custom default value
673                //
674                // field: Type,
675                //            ^
676                Some(TokenTree::Punct(p)) if p.as_char() == ',' => {
677                    // Insert default value before the comma
678                    //
679                    // field: Type = Default::default(),
680                    //             ^^^^^^^^^^^^^^^^^^^^
681                    if !is_skip {
682                        output_fields.extend(default(field_ident_span));
683                    }
684                    // field: Type = Default::default(),
685                    //                                 ^
686                    output_fields.extend(input_fields.next());
687                    // Next iteration handles the next field
688                    continue 'parse_field;
689                }
690                // This token is part of the field's type
691                //
692                // field: some::Type
693                //              ^^^^
694                Some(_) => output_fields.extend(input_fields.next()),
695                // Reached end of input, and it has no comma.
696                // This is the last field.
697                //
698                // struct Foo {
699                //     field: Type
700                //                ^
701                // }
702                None => {
703                    if !is_skip {
704                        output_fields.extend(default(field_ident_span));
705                    }
706                    // No more fields
707                    break 'parse_field;
708                }
709            }
710        }
711    }
712    let mut g = Group::new(Delimiter::Brace, output_fields);
713    g.set_span(fields.span());
714    g
715}
716
717// = ::core::default::Default::default()
718fn default(span: Span) -> [TokenTree; 14] {
719    [
720        TokenTree::Punct(Punct::new('=', Spacing::Alone)),
721        TokenTree::Punct(Punct::new(':', Spacing::Joint)).with_span(span),
722        TokenTree::Punct(Punct::new(':', Spacing::Joint)).with_span(span),
723        TokenTree::Ident(Ident::new("core", span)),
724        TokenTree::Punct(Punct::new(':', Spacing::Joint)).with_span(span),
725        TokenTree::Punct(Punct::new(':', Spacing::Joint)).with_span(span),
726        TokenTree::Ident(Ident::new("default", span)),
727        TokenTree::Punct(Punct::new(':', Spacing::Joint)).with_span(span),
728        TokenTree::Punct(Punct::new(':', Spacing::Joint)).with_span(span),
729        TokenTree::Ident(Ident::new("Default", span)),
730        TokenTree::Punct(Punct::new(':', Spacing::Joint)).with_span(span),
731        TokenTree::Punct(Punct::new(':', Spacing::Joint)).with_span(span),
732        TokenTree::Ident(Ident::new("default", span)),
733        TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())).with_span(span),
734    ]
735}
736
737macro_rules! create_compile_error {
738    ($spanned:expr, $($tt:tt)*) => {{
739        let span = if let Some(spanned) = $spanned {
740            spanned.span()
741        } else {
742            Span::call_site()
743        };
744        CompileError::new(span, format!($($tt)*))
745    }};
746}
747use create_compile_error;
748
749/// `.into_iter()` generates `compile_error!($message)` at `$span`
750struct CompileError {
751    /// Where the compile error is generates
752    pub span: Span,
753    /// Message of the compile error
754    pub message: String,
755}
756
757impl CompileError {
758    /// Create a new compile error
759    pub fn new(span: Span, message: impl AsRef<str>) -> Self {
760        Self {
761            span,
762            message: message.as_ref().to_string(),
763        }
764    }
765}
766
767impl IntoIterator for CompileError {
768    type Item = TokenTree;
769    type IntoIter = std::array::IntoIter<Self::Item, 8>;
770
771    fn into_iter(self) -> Self::IntoIter {
772        [
773            TokenTree::Punct(Punct::new(':', Spacing::Joint)).with_span(self.span),
774            TokenTree::Punct(Punct::new(':', Spacing::Joint)).with_span(self.span),
775            TokenTree::Ident(Ident::new("core", self.span)),
776            TokenTree::Punct(Punct::new(':', Spacing::Joint)).with_span(self.span),
777            TokenTree::Punct(Punct::new(':', Spacing::Joint)).with_span(self.span),
778            TokenTree::Ident(Ident::new("compile_error", self.span)),
779            TokenTree::Punct(Punct::new('!', Spacing::Alone)).with_span(self.span),
780            TokenTree::Group(Group::new(Delimiter::Brace, {
781                TokenStream::from(
782                    TokenTree::Literal(Literal::string(&self.message)).with_span(self.span),
783                )
784            }))
785            .with_span(self.span),
786        ]
787        .into_iter()
788    }
789}
790
791trait TokenTreeExt {
792    /// Set span of `TokenTree` without needing to create a new binding
793    fn with_span(self, span: Span) -> TokenTree;
794}
795
796impl TokenTreeExt for TokenTree {
797    fn with_span(mut self, span: Span) -> TokenTree {
798        self.set_span(span);
799        self
800    }
801}