-
Notifications
You must be signed in to change notification settings - Fork 13.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a macro that derives
TryFrom<u32>
for fieldless enums
- Loading branch information
Showing
6 changed files
with
187 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
use proc_macro2::TokenStream; | ||
use quote::{quote, quote_spanned}; | ||
use syn::Data; | ||
use syn::spanned::Spanned; | ||
use synstructure::Structure; | ||
|
||
pub(crate) fn try_from_u32(s: Structure<'_>) -> TokenStream { | ||
let span_error = |span, message: &str| { | ||
quote_spanned! { span => const _: () = ::core::compile_error!(#message); } | ||
}; | ||
|
||
// Must be applied to an enum type. | ||
if let Some(span) = match &s.ast().data { | ||
Data::Enum(_) => None, | ||
Data::Struct(s) => Some(s.struct_token.span()), | ||
Data::Union(u) => Some(u.union_token.span()), | ||
} { | ||
return span_error(span, "type is not an enum (TryFromU32)"); | ||
} | ||
|
||
// The enum's variants must not have fields. | ||
let variant_field_errors = s | ||
.variants() | ||
.iter() | ||
.filter_map(|v| v.ast().fields.iter().map(|f| f.span()).next()) | ||
.map(|span| span_error(span, "enum variant cannot have fields (TryFromU32)")) | ||
.collect::<TokenStream>(); | ||
if !variant_field_errors.is_empty() { | ||
return variant_field_errors; | ||
} | ||
|
||
let ctor = s | ||
.variants() | ||
.iter() | ||
.map(|v| v.construct(|_, _| -> TokenStream { unreachable!() })) | ||
.collect::<Vec<_>>(); | ||
s.gen_impl(quote! { | ||
// The surrounding code might have shadowed these identifiers. | ||
use ::core::convert::TryFrom; | ||
use ::core::primitive::u32; | ||
use ::core::result::Result::{self, Ok, Err}; | ||
|
||
gen impl TryFrom<u32> for @Self { | ||
type Error = u32; | ||
|
||
#[allow(deprecated)] // Don't warn about deprecated variants. | ||
fn try_from(value: u32) -> Result<Self, Self::Error> { | ||
#( if value == const { #ctor as u32 } { return Ok(#ctor) } )* | ||
Err(value) | ||
} | ||
} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
#![feature(rustc_private)] | ||
//@ edition: 2021 | ||
|
||
// Checks the error messages produced by `#[derive(TryFromU32)]`. | ||
|
||
extern crate rustc_macros; | ||
|
||
use rustc_macros::TryFromU32; | ||
|
||
#[derive(TryFromU32)] | ||
struct MyStruct {} //~ type is not an enum | ||
|
||
#[derive(TryFromU32)] | ||
enum NonTrivial { | ||
A, | ||
B(), | ||
C {}, | ||
D(bool), //~ enum variant cannot have fields | ||
E(bool, bool), //~ enum variant cannot have fields | ||
F { x: bool }, //~ enum variant cannot have fields | ||
G { x: bool, y: bool }, //~ enum variant cannot have fields | ||
} | ||
|
||
fn main() {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
error: type is not an enum (TryFromU32) | ||
--> $DIR/errors.rs:11:1 | ||
| | ||
LL | struct MyStruct {} | ||
| ^^^^^^ | ||
|
||
error: enum variant cannot have fields (TryFromU32) | ||
--> $DIR/errors.rs:18:7 | ||
| | ||
LL | D(bool), | ||
| ^^^^ | ||
|
||
error: enum variant cannot have fields (TryFromU32) | ||
--> $DIR/errors.rs:19:7 | ||
| | ||
LL | E(bool, bool), | ||
| ^^^^ | ||
|
||
error: enum variant cannot have fields (TryFromU32) | ||
--> $DIR/errors.rs:20:9 | ||
| | ||
LL | F { x: bool }, | ||
| ^ | ||
|
||
error: enum variant cannot have fields (TryFromU32) | ||
--> $DIR/errors.rs:21:9 | ||
| | ||
LL | G { x: bool, y: bool }, | ||
| ^ | ||
|
||
error: aborting due to 5 previous errors | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
#![feature(rustc_private)] | ||
//@ edition: 2021 | ||
//@ check-pass | ||
|
||
// Checks that the derive macro still works even if the surrounding code has | ||
// shadowed the relevant library types. | ||
|
||
extern crate rustc_macros; | ||
|
||
mod submod { | ||
use rustc_macros::TryFromU32; | ||
|
||
struct Result; | ||
trait TryFrom {} | ||
#[allow(non_camel_case_types)] | ||
struct u32; | ||
struct Ok; | ||
struct Err; | ||
mod core {} | ||
mod std {} | ||
|
||
#[derive(TryFromU32)] | ||
pub(crate) enum MyEnum { | ||
Zero, | ||
One, | ||
} | ||
} | ||
|
||
fn main() { | ||
use submod::MyEnum; | ||
let _: Result<MyEnum, u32> = MyEnum::try_from(1u32); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
#![feature(assert_matches)] | ||
#![feature(rustc_private)] | ||
//@ edition: 2021 | ||
//@ run-pass | ||
|
||
// Checks the values accepted by the `TryFrom<u32>` impl produced by `#[derive(TryFromU32)]`. | ||
|
||
extern crate rustc_macros; | ||
|
||
use core::assert_matches::assert_matches; | ||
use rustc_macros::TryFromU32; | ||
|
||
#[derive(TryFromU32, Debug, PartialEq)] | ||
#[repr(u32)] | ||
enum Repr { | ||
Zero, | ||
One(), | ||
Seven = 7, | ||
} | ||
|
||
#[derive(TryFromU32, Debug)] | ||
enum NoRepr { | ||
Zero, | ||
One, | ||
} | ||
|
||
fn main() { | ||
assert_eq!(Repr::try_from(0u32), Ok(Repr::Zero)); | ||
assert_eq!(Repr::try_from(1u32), Ok(Repr::One())); | ||
assert_eq!(Repr::try_from(2u32), Err(2)); | ||
assert_eq!(Repr::try_from(7u32), Ok(Repr::Seven)); | ||
|
||
assert_matches!(NoRepr::try_from(0u32), Ok(NoRepr::Zero)); | ||
assert_matches!(NoRepr::try_from(1u32), Ok(NoRepr::One)); | ||
assert_matches!(NoRepr::try_from(2u32), Err(2)); | ||
} |