Skip to content

Commit 4387277

Browse files
authored
Merge pull request #158 from nikomatsakis/misc-improvements
Misc improvements
2 parents 3468054 + 892387e commit 4387277

File tree

15 files changed

+186
-44
lines changed

15 files changed

+186
-44
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Constructors
2+
3+
Unless you include `#[customize(constructors)]`, the `#[term]` macro automatically creates constructors as follows:
4+
5+
- For a `struct`, defines a `new` method that takes an `impl Upcast<T>` for each of your fields.
6+
- For an `enum`, defines a method per variant that has fields (converted to snake-case).
7+
- If the name of the variant is a Rust keyword like `Struct`, the method will be called `struct_`.
8+
- We do not generate constructors for variants with no arguments.

crates/formality-check/src/where_clauses.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ impl super::Check<'_> {
5151
WhereClauseData::TypeOfConst(ct, ty) => {
5252
match ct.data() {
5353
ConstData::Value(_, t) => {
54-
self.prove_goal(in_env, &assumptions, Relation::eq(ty, t))?
54+
self.prove_goal(in_env, &assumptions, Relation::equals(ty, t))?
5555
}
5656
ConstData::Variable(_) => {}
5757
}

crates/formality-core/src/judgment.rs

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -182,18 +182,12 @@ macro_rules! push_rules {
182182
$crate::push_rules!(@match inputs() patterns() args $args);
183183
};
184184

185-
(@match inputs($in0:ident $($inputs:tt)*) patterns($pat0:ident, $($pats:tt)*) args $args:tt) => {
186-
{
187-
let $pat0 = Clone::clone($in0);
188-
$crate::push_rules!(@match inputs($($inputs)*) patterns($($pats)*) args $args);
189-
}
185+
(@match inputs() patterns $patterns:tt args $args:tt) => {
186+
compile_error!("more patterns in rule than arguments on fn")
190187
};
191188

192-
(@match inputs($in0:ident) patterns($pat0:ident) args $args:tt) => {
193-
{
194-
let $pat0 = Clone::clone($in0);
195-
$crate::push_rules!(@match inputs() patterns() args $args);
196-
}
189+
(@match inputs $inputs:tt patterns() args $args:tt) => {
190+
compile_error!("fewer patterns in rule than arguments on fn")
197191
};
198192

199193
(@match inputs($in0:ident $($inputs:tt)*) patterns($pat0:ident : $ty0:ty, $($pats:tt)*) args $args:tt) => {
@@ -204,23 +198,37 @@ macro_rules! push_rules {
204198
}
205199
};
206200

207-
(@match inputs($in0:ident) patterns($pat0:ident : $ty0:ty) args $args:tt) => {
201+
(@match inputs($in0:ident $($inputs:tt)*) patterns($pat0:ident : $ty0:ty) args $args:tt) => {
208202
{
209203
if let Some($pat0) = $crate::Downcast::downcast::<$ty0>($in0) {
210-
$crate::push_rules!(@match inputs() patterns() args $args);
204+
$crate::push_rules!(@match inputs($($inputs)*) patterns() args $args);
211205
}
212206
}
213207
};
214208

209+
(@match inputs($in0:ident $($inputs:tt)*) patterns($pat0:ident, $($pats:tt)*) args $args:tt) => {
210+
{
211+
let $pat0 = Clone::clone($in0);
212+
$crate::push_rules!(@match inputs($($inputs)*) patterns($($pats)*) args $args);
213+
}
214+
};
215+
216+
(@match inputs($in0:ident $($inputs:tt)*) patterns($pat0:ident) args $args:tt) => {
217+
{
218+
let $pat0 = Clone::clone($in0);
219+
$crate::push_rules!(@match inputs($($inputs)*) patterns() args $args);
220+
}
221+
};
222+
215223
(@match inputs($in0:ident $($inputs:tt)*) patterns($pat0:pat, $($pats:tt)*) args $args:tt) => {
216224
if let Some($pat0) = $crate::Downcast::downcast(&$in0) {
217225
$crate::push_rules!(@match inputs($($inputs)*) patterns($($pats)*) args $args);
218226
}
219227
};
220228

221-
(@match inputs($in0:ident) patterns($pat0:pat) args $args:tt) => {
229+
(@match inputs($in0:ident $($inputs:tt)*) patterns($pat0:pat) args $args:tt) => {
222230
if let Some($pat0) = $crate::Downcast::downcast(&$in0) {
223-
$crate::push_rules!(@match inputs() patterns() args $args);
231+
$crate::push_rules!(@match inputs($($inputs)*) patterns() args $args);
224232
}
225233
};
226234

@@ -257,7 +265,9 @@ macro_rules! push_rules {
257265
};
258266

259267
(@body $args:tt ($i:expr => $p:pat) $($m:tt)*) => {
260-
for $p in $i {
268+
// Explicitly calling `into_iter` silences some annoying lints
269+
// in the case where `$i` is an `Option` or a `Result`
270+
for $p in std::iter::IntoIterator::into_iter($i) {
261271
$crate::push_rules!(@body $args $($m)*);
262272
}
263273
};

crates/formality-core/src/parse/parser.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -652,8 +652,9 @@ where
652652
None => Err(ParseError::at(
653653
text0,
654654
format!(
655-
"wrong kind, expected a {}, found a {:?}",
655+
"wrong kind, expected a {}, found `{:?}`, which is a `{:?}`",
656656
type_name,
657+
parameter,
657658
parameter.kind()
658659
),
659660
)),
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

crates/formality-macros/src/cast.rs

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
use proc_macro2::TokenStream;
2-
use quote::quote;
1+
use convert_case::{Case, Casing};
2+
use proc_macro2::{Ident, TokenStream};
3+
use quote::{quote, quote_spanned};
34
use syn::Type;
45
use synstructure::VariantInfo;
56

@@ -79,7 +80,7 @@ fn downcast_to_variant(s: &synstructure::Structure, v: &VariantInfo) -> TokenStr
7980
}
8081
});
8182

82-
s.gen_impl(quote! {
83+
let mut ts = s.gen_impl(quote! {
8384
use formality_core::{DowncastTo};
8485

8586
gen impl DowncastTo<(#(#binding_tys),*)> for @Self {
@@ -89,5 +90,44 @@ fn downcast_to_variant(s: &synstructure::Structure, v: &VariantInfo) -> TokenStr
8990
}
9091
}
9192
}
92-
})
93+
});
94+
95+
if binding_tys.len() == 1 && matches!(s.ast().data, syn::Data::Enum(_)) {
96+
let binding_ty = &binding_tys[0];
97+
let type_name = &s.ast().ident;
98+
let variant_name = &v.ast().ident;
99+
let as_method_name = Ident::new(
100+
&format!("as_{}", variant_name.to_string().to_case(Case::Snake)),
101+
v.ast().ident.span(),
102+
);
103+
let (impl_generics, type_generics, where_clauses) = s.ast().generics.split_for_impl();
104+
105+
let match_arms = s.each_variant(|variant_info| {
106+
if variant_info.ast().ident == v.ast().ident {
107+
let bindings = variant_info.bindings();
108+
let binding_idents: Vec<&syn::Ident> =
109+
bindings.iter().map(|b| &b.binding).collect();
110+
quote! { Some((#(#binding_idents),*)) }
111+
} else {
112+
quote! { None }
113+
}
114+
});
115+
116+
let as_impl = quote_spanned! { v.ast().ident.span() =>
117+
#[allow(dead_code)]
118+
impl #impl_generics #type_name #type_generics
119+
where #where_clauses
120+
{
121+
pub fn #as_method_name(&self) -> Option<& #binding_ty> {
122+
match self {
123+
#match_arms
124+
}
125+
}
126+
}
127+
};
128+
129+
ts.extend(as_impl);
130+
}
131+
132+
ts
93133
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
use convert_case::{Case, Casing};
2+
use proc_macro2::{Ident, TokenStream};
3+
use quote::quote_spanned;
4+
use synstructure::VariantInfo;
5+
6+
const RUST_KEYWORDS: &[&str] = &[
7+
"mut", "true", "false", "const", "static", "ref", "struct", "enum", "trait", "union", "fn",
8+
"use", "return", "move", "let", "break", "loop", "continue", "await", "if", "for", "unsafe",
9+
];
10+
11+
/// Create methods to build this type.
12+
///
13+
/// For a struct, we create a single `new` method that takes each field.
14+
///
15+
/// For an enum, we create methods named after each variant.
16+
pub(crate) fn constructor_methods(s: synstructure::Structure) -> TokenStream {
17+
match s.ast().data {
18+
syn::Data::Struct(_) => derive_new_for_struct(s),
19+
syn::Data::Enum(_) => derive_new_for_variants(s),
20+
syn::Data::Union(_) => return Default::default(),
21+
}
22+
}
23+
24+
fn derive_new_for_struct(s: synstructure::Structure<'_>) -> TokenStream {
25+
derive_new_for_variant(
26+
&s,
27+
&s.variants()[0],
28+
&Ident::new("new", s.ast().ident.span()),
29+
)
30+
}
31+
32+
fn derive_new_for_variants(s: synstructure::Structure<'_>) -> TokenStream {
33+
s.variants()
34+
.iter()
35+
.map(|v| {
36+
let mut fn_name = v.ast().ident.to_string().to_case(Case::Snake);
37+
if RUST_KEYWORDS.iter().any(|&kw| kw == fn_name) {
38+
fn_name.push('_');
39+
}
40+
let fn_name = Ident::new(&fn_name, v.ast().ident.span());
41+
derive_new_for_variant(&s, v, &fn_name)
42+
})
43+
.collect()
44+
}
45+
46+
fn derive_new_for_variant(
47+
s: &synstructure::Structure<'_>,
48+
v: &VariantInfo<'_>,
49+
fn_name: &Ident,
50+
) -> TokenStream {
51+
let type_name = &s.ast().ident;
52+
let (impl_generics, type_generics, where_clauses) = s.ast().generics.split_for_impl();
53+
54+
// If there are no bindings, not worth it.
55+
if v.bindings().is_empty() {
56+
return TokenStream::default();
57+
}
58+
59+
let binding_names = v.bindings().iter().map(|b| &b.binding).collect::<Vec<_>>();
60+
let binding_types = v.bindings().iter().map(|b| &b.ast().ty).collect::<Vec<_>>();
61+
let construct = v.construct(|_b, i| {
62+
let name = binding_names[i];
63+
quote_spanned!(
64+
binding_names[i].span() =>
65+
formality_core::Upcast::upcast(#name)
66+
)
67+
});
68+
69+
quote_spanned! { v.ast().ident.span() =>
70+
#[allow(dead_code)]
71+
impl #impl_generics #type_name #type_generics
72+
where #where_clauses
73+
{
74+
pub fn #fn_name(
75+
#(
76+
#binding_names: impl formality_core::Upcast<#binding_types>,
77+
)*
78+
) -> Self {
79+
#construct
80+
}
81+
}
82+
}
83+
}

crates/formality-macros/src/custom.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use proc_macro2::TokenStream;
44
pub(crate) struct Customize {
55
pub parse: bool,
66
pub debug: bool,
7+
pub constructors: bool,
78
}
89

910
impl syn::parse::Parse for Customize {
@@ -28,6 +29,13 @@ impl syn::parse::Parse for Customize {
2829
result.debug = true;
2930
}
3031

32+
proc_macro2::TokenTree::Ident(ident) if ident == "constructors" => {
33+
if result.constructors {
34+
return Err(syn::Error::new(ident.span(), "already customizing debug"));
35+
}
36+
result.constructors = true;
37+
}
38+
3139
_ => {
3240
return Err(syn::Error::new(
3341
token.span(),

crates/formality-macros/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ use spec::FormalitySpec;
55

66
extern crate proc_macro;
77

8+
mod as_methods;
89
mod attrs;
910
mod cast;
11+
mod constructors;
1012
mod custom;
1113
mod debug;
1214
mod fixed_point;

crates/formality-macros/src/term.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use syn::DeriveInput;
55
use crate::{
66
attrs::{self, remove_formality_attributes},
77
cast::{downcast_impls, upcast_impls},
8+
constructors::constructor_methods,
89
debug::derive_debug_with_spec,
910
fold::derive_fold,
1011
parse::derive_parse_with_spec,
@@ -35,6 +36,11 @@ pub fn term(spec: Option<FormalitySpec>, mut input: DeriveInput) -> syn::Result<
3536
let term_impl = derive_term(synstructure::Structure::new(&input));
3637
let downcast_impls = downcast_impls(synstructure::Structure::new(&input));
3738
let upcast_impls = upcast_impls(synstructure::Structure::new(&input));
39+
let constructors = if customize.constructors {
40+
None
41+
} else {
42+
Some(constructor_methods(synstructure::Structure::new(&input)))
43+
};
3844
remove_formality_attributes(&mut input);
3945

4046
Ok(quote! {
@@ -48,6 +54,7 @@ pub fn term(spec: Option<FormalitySpec>, mut input: DeriveInput) -> syn::Result<
4854
#term_impl
4955
#(#downcast_impls)*
5056
#(#upcast_impls)*
57+
#constructors
5158
})
5259
}
5360

0 commit comments

Comments
 (0)