Skip to content

Commit f11b352

Browse files
committed
macros: add support for exhaustive attr
Add support for `exhaustive` custom attribute to opt out of having the record have a `__non_exhaustive` field added to it. Modify `struct_with_default` to automatically add a private field to all records such that they are always built field-by-field in downstream crates.
1 parent bcc2a05 commit f11b352

2 files changed

Lines changed: 119 additions & 22 deletions

File tree

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@
152152
// Not all macros and all patterns are used on all targets.
153153
#![allow(unused_macros)]
154154
#![allow(unused_macro_rules)]
155+
// FIXME(rust-lang/rust#132699): get rid of this once we stop using the private
156+
// field.
157+
#![allow(clippy::manual_non_exhaustive)]
155158
// All traits should be `Copy` and `Debug`.
156159
#![warn(missing_copy_implementations)]
157160
#![warn(missing_debug_implementations)]

src/macros.rs

Lines changed: 116 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ macro_rules! s {
163163
)*);
164164

165165
(it: $(#[$attr:meta])* $pub:vis union $i:ident { $($field:tt)* }) => (
166-
compile_error!("unions cannot derive extra traits, use s_no_extra_traits instead");
166+
::core::compile_error!("unions cannot derive extra traits, use s_no_extra_traits instead");
167167
);
168168

169169
(it: $(#[$attr:meta])* $pub:vis struct $i:ident { $($field:tt)* }) => (
@@ -253,19 +253,19 @@ macro_rules! s_no_extra_traits {
253253
/// Like [`s`], but also generates a `Default` impl for every struct in the block.
254254
macro_rules! s_with_default {
255255
($(
256-
$(#[$attr:meta])*
256+
$(#$attr:tt)*
257257
$pub:vis $t:ident $i:ident { $($field:tt)* }
258258
)*) => ($(
259-
s_with_default!(it: $(#[$attr])* $pub $t $i { $($field)* });
259+
s_with_default!(it: $(#$attr)* $pub $t $i { $($field)* });
260260
)*);
261261

262-
(it: $(#[$attr:meta])* $pub:vis union $i:ident { $($field:tt)* }) => (
263-
compile_error!(
262+
(it: $(#$attr:tt)* $pub:vis union $i:ident { $($field:tt)* }) => (
263+
::core::compile_error!(
264264
"unions cannot derive extra traits, use s_no_extra_traits_with_default instead"
265265
);
266266
);
267267

268-
(it: $(#[$attr:meta])* $pub:vis struct $i:ident { $($field:tt)* }) => (
268+
(it: $(#$attr:tt)* $pub:vis struct $i:ident { $($field:tt)* }) => (
269269
struct_with_default! {
270270
attrs: {
271271
#[repr(C)]
@@ -280,7 +280,7 @@ macro_rules! s_with_default {
280280
)]
281281
#[allow(deprecated)]
282282
}
283-
$(#[$attr])* $pub struct $i { $($field)* }
283+
$(#$attr)* $pub struct $i { $($field)* }
284284
}
285285
);
286286
}
@@ -291,19 +291,19 @@ macro_rules! s_with_default {
291291
/// union type supplies its own default via `#[custom_default(...)]`.
292292
macro_rules! s_no_extra_traits_with_default {
293293
($(
294-
$(#[$attr:meta])*
294+
$(#$attr:tt)*
295295
$pub:vis $t:ident $i:ident { $($field:tt)* }
296296
)*) => ($(
297-
s_no_extra_traits_with_default!(it: $(#[$attr])* $pub $t $i { $($field)* });
297+
s_no_extra_traits_with_default!(it: $(#$attr)* $pub $t $i { $($field)* });
298298
)*);
299299

300-
(it: $(#[$attr:meta])* $pub:vis union $i:ident { $($field:tt)* }) => (
300+
(it: $(#$attr:tt)* $pub:vis union $i:ident { $($field:tt)* }) => (
301301
#[repr(C)]
302302
#[::core::prelude::v1::derive(
303303
::core::clone::Clone,
304304
::core::marker::Copy,
305305
)]
306-
$(#[$attr])*
306+
$(#$attr)*
307307
$pub union $i { $($field)* }
308308

309309
impl ::core::fmt::Debug for $i {
@@ -313,7 +313,7 @@ macro_rules! s_no_extra_traits_with_default {
313313
}
314314
);
315315

316-
(it: $(#[$attr:meta])* $pub:vis struct $i:ident { $($field:tt)* }) => (
316+
(it: $(#$attr:tt)* $pub:vis struct $i:ident { $($field:tt)* }) => (
317317
struct_with_default! {
318318
attrs: {
319319
#[repr(C)]
@@ -323,7 +323,7 @@ macro_rules! s_no_extra_traits_with_default {
323323
::core::fmt::Debug,
324324
)]
325325
}
326-
$(#[$attr])* $pub struct $i { $($field)* }
326+
$(#$attr)* $pub struct $i { $($field)* }
327327
}
328328
);
329329
}
@@ -343,12 +343,12 @@ macro_rules! struct_with_default {
343343
// which is merged with the struct's own attributes.
344344
(
345345
attrs: { $($attrs:tt)* }
346-
$(#[$attr:meta])*
346+
$(#$attr:tt)*
347347
$vis:vis struct $name:ident { $($body:tt)* }
348348
) => {
349349
struct_with_default! {
350350
@struct
351-
attrs: { $($attrs)* $(#[$attr])* }
351+
attrs: { $($attrs)* $(#$attr)* }
352352
vis: { $vis }
353353
name: { $name }
354354
processed_fields: { }
@@ -414,7 +414,7 @@ macro_rules! struct_with_default {
414414
}
415415
};
416416

417-
// done
417+
// done with `Default`, start scanning for the `exhaustive` attribute
418418
(
419419
@struct
420420
attrs: { $($attrs:tt)* }
@@ -424,18 +424,112 @@ macro_rules! struct_with_default {
424424
processed_field_defaults: { $($processed_field_defaults:tt)* }
425425
remaining_fields: { }
426426
) => {
427-
$($attrs)*
428-
$vis struct $name { $($processed_fields)* }
427+
struct_with_default! {
428+
@attr
429+
attrs: { $($attrs)* }
430+
processed_attrs: { }
431+
found: { false }
432+
vis: { $vis }
433+
name: { $name }
434+
processed_fields: { $($processed_fields)* }
435+
processed_field_defaults: { $($processed_field_defaults)* }
436+
}
437+
};
438+
439+
// done with `Default` and `exhaustive`; we hand off handling the basecase
440+
// of either adding the private field or not to the `fin` macro.
441+
(
442+
@attr
443+
attrs: { }
444+
processed_attrs: { $($attrs:tt)* }
445+
found: { $found:tt }
446+
vis: { $vis:vis }
447+
name: { $name:ident }
448+
processed_fields: { $($processed_fields:tt)* }
449+
processed_field_defaults: { $($processed_field_defaults:tt)* }
450+
) => {
451+
fin! { $found decl: $($attrs)* $vis $name { $($processed_fields)* } }
429452

430453
impl ::core::default::Default for $name {
431-
// Field attributes (`#[cfg]`, doc comments) get forwarded to the initializer too.
432-
// Docs are harmless there but trip the lint, so silence it.
454+
// Field attributes (`#[cfg]`, doc comments) get forwarded to the
455+
// initializer too. Docs are harmless there but trip the lint, so
456+
// silence it.
433457
#[allow(unused_doc_comments)]
434458
fn default() -> Self {
435-
Self { $($processed_field_defaults)* }
459+
fin! { $found default: { $($processed_field_defaults)* } }
436460
}
437461
}
438462
};
463+
464+
// found `exhaustive`; parameter `found` is not matched against `false` in
465+
// case somebody annotates a `struct` with more than one `exhaustive`. If we
466+
// didn't match a wildcard, that attribute would go down to the general case
467+
// of the muncher and add it to the packed matcher of `processed_attrs`.
468+
(
469+
@attr
470+
attrs: { #[exhaustive] $($attrs:tt)* }
471+
processed_attrs: { $($prev_attrs:tt)* }
472+
found: { $_:tt }
473+
vis: { $vis:vis }
474+
name: { $name:ident }
475+
processed_fields: { $($processed_fields:tt)* }
476+
processed_field_defaults: { $($processed_field_defaults:tt)* }
477+
) => {
478+
struct_with_default! {
479+
@attr
480+
attrs: { $($attrs)* }
481+
processed_attrs: { $($prev_attrs)* }
482+
found: { true }
483+
vis: { $vis }
484+
name: { $name }
485+
processed_fields: { $($processed_fields)* }
486+
processed_field_defaults: { $($processed_field_defaults)* }
487+
}
488+
};
489+
490+
// we've not found `exhaustive`; we keep munching to see whether it's still
491+
// there
492+
(
493+
@attr
494+
attrs: { #$other:tt $($attrs:tt)* }
495+
processed_attrs: { $($prev_attrs:tt)* }
496+
found: { $found:tt }
497+
vis: { $vis:vis }
498+
name: { $name:ident }
499+
processed_fields: { $($processed_fields:tt)* }
500+
processed_field_defaults: { $($processed_field_defaults:tt)* }
501+
) => {
502+
struct_with_default! {
503+
@attr
504+
attrs: { $($attrs)* }
505+
processed_attrs: { $($prev_attrs)* #$other }
506+
found: { $found }
507+
vis: { $vis }
508+
name: { $name }
509+
processed_fields: { $($processed_fields)* }
510+
processed_field_defaults: { $($processed_field_defaults)* }
511+
}
512+
};
513+
}
514+
515+
/// Helper macro for `struct_with_default`. Prints the basecase item with either
516+
/// the private `non_exhaustive` field or not.
517+
macro_rules! fin {
518+
(true decl: $(#[$attr:meta])* $vis:vis $name:ident { $($field:tt)* }) => {
519+
$(#[$attr])*
520+
$vis struct $name { $($field)* }
521+
};
522+
(true default: { $($field_default:tt)* }) => {
523+
Self { $($field_default)* }
524+
};
525+
526+
(false decl: $(#[$attr:meta])* $vis:vis $name:ident { $($field:tt)* }) => {
527+
$(#[$attr])*
528+
$vis struct $name { $($field)* __non_exhaustive: () }
529+
};
530+
(false default: { $($field_default:tt)* }) => {
531+
Self { $($field_default)* __non_exhaustive: () }
532+
};
439533
}
440534

441535
/// Create an uninhabited type that can't be constructed. It implements `Debug`, `Clone`,
@@ -620,7 +714,7 @@ macro_rules! f {
620714
fn $i:ident ($($arg:ident: $argty:ty),* $(,)?) -> $ret:ty
621715
$body:block
622716
) => {
623-
compile_error!("either `safe` or `unsafe` must be specified");
717+
::core::compile_error!("either `safe` or `unsafe` must be specified");
624718
};
625719
}
626720

0 commit comments

Comments
 (0)