Skip to content

Commit 606cb2c

Browse files
committed
extract the attrs code and better document meaning
1 parent 2fcc2d6 commit 606cb2c

File tree

7 files changed

+71
-34
lines changed

7 files changed

+71
-34
lines changed

crates/formality-macros/src/attrs.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//! Functions to manipulate the custom attributes that guide our macros in various ways.
2+
3+
use syn::{Attribute, DeriveInput};
4+
5+
/// Checks for any kind of attribute that indicates an "is-a" relationship,
6+
/// e.g. `#[cast]` and `#[variable]`.
7+
///
8+
/// See [`has_cast_attr`][] and [`has_variable_attr`][] for more details.
9+
pub(crate) fn has_isa_attr(attrs: &[Attribute]) -> bool {
10+
has_cast_attr(attrs) || has_variable_attr(attrs)
11+
}
12+
13+
/// The `#[cast]` attribute is placed on enum variants with a single binding,
14+
/// like `enum Foo { #[cast] Bar(Bar), ... }`. It indicates that `Foo` is an
15+
/// extension of `Bar`, so we should permit upcasts from `Bar` to `Foo`
16+
/// and generally ignore the fact that `Bar` is a variant of `Foo` (for example,
17+
/// in debug print-outs, we don't print `Bar(...)`, we just print `...`).
18+
pub(crate) fn has_cast_attr(attrs: &[Attribute]) -> bool {
19+
attrs.iter().any(|a| a.path().is_ident("cast"))
20+
}
21+
22+
/// The `#[variable]` attribute is a special-case of `#[cast]` that is used
23+
/// for variables. It can only be used on a single-entry variant in some type
24+
/// that is up/down-castable to the language's `Parameter` type (e.g., in Rust,
25+
/// this could be used in `Ty`, `Lifetime`, or `Const`).
26+
///
27+
/// `#[variable]` has subtle effects on folding and parsing:
28+
///
29+
/// * When folding a variable variant, we apply the substitution function, which yields
30+
/// a Parameter. We then downcast that to the type needed. If that downcast
31+
/// fails, we panic, as that indicates an ill-kinded substitution.
32+
/// * When parsing a variable variant, we parse an identifier and then check the in-scope
33+
/// bindings for variables with that name. If any are found, we extract the result
34+
/// (a parameter) and downcast it. If the resulting downcast fails, that is considered
35+
/// a parse error (ill-kinded term).
36+
pub(crate) fn has_variable_attr(attrs: &[Attribute]) -> bool {
37+
attrs.iter().any(|a| a.path().is_ident("variable"))
38+
}
39+
40+
/// Removes all attributes from the input that are specific to formality.
41+
pub(crate) fn remove_formality_attributes(input: &mut DeriveInput) {
42+
remove_formality_attributes_from_vec(&mut input.attrs);
43+
if let syn::Data::Enum(v) = &mut input.data {
44+
for variant in &mut v.variants {
45+
remove_formality_attributes_from_vec(&mut variant.attrs);
46+
}
47+
}
48+
}
49+
50+
fn remove_formality_attributes_from_vec(attrs: &mut Vec<Attribute>) {
51+
attrs.retain(|attr| {
52+
!attr.path().is_ident("grammar")
53+
&& !attr.path().is_ident("cast")
54+
&& !attr.path().is_ident("variable")
55+
});
56+
}

crates/formality-macros/src/cast.rs

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
use proc_macro2::TokenStream;
22
use quote::quote;
3-
use syn::{Attribute, Type};
3+
use syn::Type;
44
use synstructure::VariantInfo;
55

6-
use crate::term::has_variable_attr;
6+
use crate::attrs::has_isa_attr;
77

88
pub(crate) fn upcast_impls(s: synstructure::Structure) -> Vec<TokenStream> {
99
let num_variants = s.variants().len();
1010
s.variants()
1111
.iter()
12-
.filter(|v| {
13-
num_variants == 1 || has_cast_attr(v.ast().attrs) || has_variable_attr(v.ast().attrs)
14-
})
12+
.filter(|v| num_variants == 1 || has_isa_attr(v.ast().attrs))
1513
.map(|v| upcast_to_variant(&s, v))
1614
.chain(Some(self_upcast(&s)))
1715
.collect()
@@ -50,9 +48,7 @@ pub(crate) fn downcast_impls(s: synstructure::Structure) -> Vec<TokenStream> {
5048
let num_variants = s.variants().len();
5149
s.variants()
5250
.iter()
53-
.filter(|v| {
54-
num_variants == 1 || has_cast_attr(v.ast().attrs) || has_variable_attr(v.ast().attrs)
55-
})
51+
.filter(|v| num_variants == 1 || has_isa_attr(v.ast().attrs))
5652
.map(|v| downcast_to_variant(&s, v))
5753
.chain(Some(self_downcast(&s)))
5854
.collect()
@@ -95,7 +91,3 @@ fn downcast_to_variant(s: &synstructure::Structure, v: &VariantInfo) -> TokenStr
9591
}
9692
})
9793
}
98-
99-
pub(crate) fn has_cast_attr(attrs: &[Attribute]) -> bool {
100-
attrs.iter().any(|a| a.path().is_ident("cast"))
101-
}

crates/formality-macros/src/debug.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ use proc_macro2::{Ident, Literal, TokenStream};
44
use quote::{quote, quote_spanned};
55
use syn::{spanned::Spanned, Attribute};
66

7-
use crate::spec::{self, FieldMode, FormalitySpec, FormalitySpecOp};
7+
use crate::{
8+
attrs,
9+
spec::{self, FieldMode, FormalitySpec, FormalitySpecOp},
10+
};
811

912
/// Derive the `Parse` impl, using an optional grammar supplied "from the outside".
1013
/// This is used by the `#[term(G)]` macro, which supplies the grammar `G`.
@@ -62,7 +65,7 @@ fn debug_variant(
6265
quote! {
6366
write!(fmt, #literal)?;
6467
}
65-
} else if crate::cast::has_cast_attr(variant.ast().attrs) {
68+
} else if attrs::has_isa_attr(variant.ast().attrs) {
6669
let streams: Vec<_> = variant
6770
.bindings()
6871
.iter()

crates/formality-macros/src/fold.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ extern crate proc_macro;
33
use proc_macro2::TokenStream;
44
use quote::quote;
55

6-
use crate::term::has_variable_attr;
6+
use crate::attrs::has_variable_attr;
77

88
pub(crate) fn derive_fold(mut s: synstructure::Structure) -> TokenStream {
99
s.underscore_const(true);

crates/formality-macros/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use spec::FormalitySpec;
55

66
extern crate proc_macro;
77

8+
mod attrs;
89
mod cast;
910
mod debug;
1011
mod fixed_point;

crates/formality-macros/src/parse.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use synstructure::BindingInfo;
77

88
use crate::{
99
spec::{self, FieldMode, FormalitySpec, FormalitySpecOp},
10-
term::has_variable_attr,
10+
attrs::{has_variable_attr, has_cast_attr},
1111
};
1212

1313
/// Derive the `Parse` impl, using an optional grammar supplied "from the outside".
@@ -108,7 +108,7 @@ fn parse_variant(
108108
ast.ident.span() =>
109109
parse::parse_variable(scope, text, #type_name)
110110
})
111-
} else if crate::cast::has_cast_attr(variant.ast().attrs) {
111+
} else if has_cast_attr(variant.ast().attrs) {
112112
// Has the `#[cast]` attribute -- just parse the bindings (comma separated, if needed)
113113
let build: Vec<TokenStream> = parse_bindings(variant.bindings());
114114
let construct = variant.construct(field_ident);

crates/formality-macros/src/term.rs

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use proc_macro2::TokenStream;
22
use quote::quote;
3-
use syn::{Attribute, DeriveInput};
3+
use syn::DeriveInput;
44

55
use crate::{
6+
attrs::remove_formality_attributes,
67
cast::{downcast_impls, upcast_impls},
78
debug::derive_debug_with_spec,
89
fold::derive_fold,
@@ -35,18 +36,6 @@ pub fn term(spec: Option<FormalitySpec>, mut input: DeriveInput) -> syn::Result<
3536
})
3637
}
3738

38-
fn remove_formality_attributes(input: &mut DeriveInput) {
39-
if let syn::Data::Enum(v) = &mut input.data {
40-
for variant in &mut v.variants {
41-
variant.attrs.retain(|attr| {
42-
!attr.path().is_ident("grammar")
43-
&& !attr.path().is_ident("cast")
44-
&& !attr.path().is_ident("variable")
45-
});
46-
}
47-
}
48-
}
49-
5039
fn derive_term(mut s: synstructure::Structure) -> TokenStream {
5140
s.underscore_const(true);
5241
s.bind_with(|_| synstructure::BindStyle::Move);
@@ -59,7 +48,3 @@ fn derive_term(mut s: synstructure::Structure) -> TokenStream {
5948
}
6049
})
6150
}
62-
63-
pub(crate) fn has_variable_attr(attrs: &[Attribute]) -> bool {
64-
attrs.iter().any(|a| a.path().is_ident("variable"))
65-
}

0 commit comments

Comments
 (0)