Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(#5,#8): port generics from previous branch
Browse files Browse the repository at this point in the history
José Duarte committed Dec 8, 2021
1 parent f860fed commit d08b4e7
Showing 6 changed files with 108 additions and 80 deletions.
20 changes: 20 additions & 0 deletions examples/generics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use typestate::typestate;

#[typestate]
mod my_state {
#[automaton]
pub struct MyState;

#[state]
pub struct State1<T> {
data: T,
}

trait State1 {
fn new<T>() -> State1<T>;

fn done(self);
}
}

fn main() {}
6 changes: 1 addition & 5 deletions typestate-proc-macro/src/igraph/export.rs
Original file line number Diff line number Diff line change
@@ -421,11 +421,7 @@ pub mod dot {
}

#[cfg(any(feature = "dot", feature = "plantuml"))]
use {
super::IntermediateGraph,
std::fs::File,
syn::Ident
};
use {super::IntermediateGraph, std::fs::File, syn::Ident};

#[cfg(any(feature = "dot", feature = "plantuml"))]
pub(crate) fn export<F: Format>(
1 change: 0 additions & 1 deletion typestate-proc-macro/src/lib.rs
Original file line number Diff line number Diff line change
@@ -211,7 +211,6 @@ fn export_diagram_files(state_machine_info: &StateMachineInfo) {
}
}


trait ExpandEnumerate {
fn expand_enumerate(&mut self, automata: &ItemStruct, automata_enum: &Ident, states: &[&Ident]);
/// Expand the [`ToString`] implentation for enumeration.
21 changes: 14 additions & 7 deletions typestate-proc-macro/src/visitors/decision.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use std::collections::HashSet;

use darling::FromMeta;
use syn::{visit_mut::VisitMut, Error, Fields, Ident, ItemEnum, ItemMod, Variant};
use syn::{visit_mut::VisitMut, Error, Fields, Generics, Ident, ItemEnum, ItemMod, Variant};

use crate::{
igraph::{Metadata, StateNode},
@@ -31,19 +33,22 @@ pub(crate) fn visit_non_deterministic(

struct DecisionVisitor<'sm> {
state_machine_info: &'sm mut StateMachineInfo,
decision_generics: HashSet<Generics>,
errors: Vec<Error>,
}

impl<'sm> DecisionVisitor<'sm> {
fn new(state_machine_info: &'sm mut StateMachineInfo) -> Self {
Self {
state_machine_info,
decision_generics: HashSet::new(),
errors: vec![],
}
}

fn visit_variant_mut(&mut self, variant: &mut Variant) -> Option<StateNode<Ident>> {
if let Fields::Unit = &variant.fields {
let det_states = &self.state_machine_info.det_states;
let ident = &variant.ident;
// check if the current ident is a valid state or another decision node
if self
@@ -52,32 +57,34 @@ impl<'sm> DecisionVisitor<'sm> {
.contains_key(ident)
{
self.push_unsupported_state_error(ident);
} else if self.state_machine_info.det_states.contains_key(ident) {
} else if let Some(it_struct) = det_states.get(ident) {
let mut state = StateNode::new(Some(ident.clone()));

let mut errors = vec![];
variant.attrs.retain(|attr| {
if attr.path.is_ident("metadata") {
match attr.parse_meta() {
Ok(meta) => match Metadata::from_meta(&meta) {
Ok(metadata) => state.update_metadata(metadata),
Err(err) => {
// TODO fix this hack
// HACK
self.errors.push(Error::new_spanned(attr, err.to_string()))
errors.push(Error::new_spanned(attr, err.to_string()))
}
},
Err(err) => self.errors.push(err),
Err(err) => errors.push(err),
}
false
} else {
true
}
});
self.errors.append(&mut errors);

let automata_ident = self.state_machine_info.get_automaton_ident();
let generics = &it_struct.generics;
self.decision_generics.insert(generics.clone());
variant.fields = Fields::Unnamed(::syn::parse_quote!(
/* Variant */ (
#automata_ident<#ident>
#automata_ident<#ident #generics>
)
));

131 changes: 66 additions & 65 deletions typestate-proc-macro/src/visitors/state.rs
Original file line number Diff line number Diff line change
@@ -93,69 +93,6 @@ impl<'sm> StateVisitor<'sm> {
}
}

#[derive(Default)]
pub(crate) struct SealedPattern {
/// Ident for the sealed pattern public trait
trait_ident: Option<Ident>, // late init
/// Idents for the sealed elements.
state_idents: Vec<Ident>,
}

// TODO rework this as an ExpandX trait
impl From<SealedPattern> for Vec<Item> {
/// Convert the [`SealedTrait`] into a vector of Item.
/// This enables the addition of new items to the main module.
fn from(sealed_pattern: SealedPattern) -> Self {
let trait_ident = sealed_pattern.trait_ident.expect("missing `.trait_ident`");
let private_mod_ident = ::quote::format_ident!("__private");
// or `Private` or `Sealed` or `format_ident!("{}Sealed", …)`
// take into account that `trait_ident` may have already been used
let private_mod_trait = &trait_ident;

let generated_attr = generated_attr();

let states = &sealed_pattern.state_idents;
let mut ret = vec![
// Sealed trait
::syn::parse_quote! {
#generated_attr
#[doc(hidden)]
/* private */ mod #private_mod_ident {
/* to avoid the nested item being processed */
#generated_attr
pub trait #private_mod_trait {}
}
},
// State trait
::syn::parse_quote! {
#generated_attr
pub trait #trait_ident: #private_mod_ident::#private_mod_trait {}
},
// Blanket impl of state trait from sealed implementors
// This frees us from having to provide concrete impls for each type.
::syn::parse_quote! {
#generated_attr
impl<__T : ?::core::marker::Sized> #trait_ident
for __T
where
__T : #private_mod_ident::#private_mod_trait,
{}
},
];

// Sealed trait impls
ret.extend(states.iter().map(|each_state| {
::syn::parse_quote! {
#generated_attr
impl #private_mod_ident::#private_mod_trait for #each_state {}
}
}));

// TODO: this can probably be turned into a single parse_quote!
ret
}
}

impl<'sm> VisitMut for StateVisitor<'sm> {
fn visit_item_struct_mut(&mut self, it_struct: &mut ItemStruct) {
let attributes = &mut it_struct.attrs;
@@ -211,14 +148,13 @@ impl<'sm> VisitMut for StateVisitor<'sm> {
}
}
Some(TypestateAttr::State) => {
// BOOK: intermediate_automaton.add_state
self.state_machine_info
.intermediate_automaton
.add_state(it_struct.ident.clone());

// TODO: remove the call below
self.state_machine_info.add_state(it_struct.clone().into());
self.sealed_trait.state_idents.push(it_struct.ident.clone());
self.sealed_trait.states.push(it_struct.clone());
if let Some(ident) = &self.constructor_ident {
self.constructors
.expand_state_constructors(ident, it_struct);
@@ -332,3 +268,68 @@ enum Attr {
Retain,
Discard,
}

#[derive(Default)]
pub(crate) struct SealedPattern {
/// Ident for the sealed pattern public trait
trait_ident: Option<Ident>, // late init
/// Item structs for the states.
states: Vec<ItemStruct>,
}

// TODO rework this as an ExpandX trait
impl From<SealedPattern> for Vec<Item> {
/// Convert the [`SealedTrait`] into a vector of Item.
/// This enables the addition of new items to the main module.
fn from(sealed_pattern: SealedPattern) -> Self {
let trait_ident = sealed_pattern.trait_ident.expect("missing `.trait_ident`");
let private_mod_ident = ::quote::format_ident!("__private");
// or `Private` or `Sealed` or `format_ident!("{}Sealed", …)`
// take into account that `trait_ident` may have already been used
let private_mod_trait = &trait_ident;

let generated_attr = generated_attr();

let mut ret = vec![
// Sealed trait
::syn::parse_quote! {
#generated_attr
#[doc(hidden)]
/* private */ mod #private_mod_ident {
/* to avoid the nested item being processed */
#generated_attr
pub trait #private_mod_trait {}
}
},
// State trait
::syn::parse_quote! {
#generated_attr
pub trait #trait_ident: #private_mod_ident::#private_mod_trait {}
},
// Blanket impl of state trait from sealed implementors
// This frees us from having to provide concrete impls for each type.
::syn::parse_quote! {
#generated_attr
impl<__T : ?::core::marker::Sized> #trait_ident
for __T
where
__T : #private_mod_ident::#private_mod_trait,
{}
},
];

let states = &sealed_pattern.states;

// Sealed trait impls
ret.extend(states.iter().map(|each_state| {
let struct_ident = &each_state.ident;
let (impl_generics, type_generics, where_clause) = each_state.generics.split_for_impl();
::syn::parse_quote! {
#generated_attr
impl #impl_generics #private_mod_ident::#private_mod_trait for #struct_ident #type_generics #where_clause {}
}
}));

ret
}
}
9 changes: 7 additions & 2 deletions typestate-proc-macro/src/visitors/transition.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::collections::HashSet;

use crate::{StateMachineInfo, Transition, TypestateError, IsGeneratedAttr};
use crate::{IsGeneratedAttr, StateMachineInfo, Transition, TypestateError};
use syn::Attribute;
use syn::{
visit_mut::VisitMut, Error, FnArg, Ident, ItemMod, ItemTrait, Receiver, ReturnType, Signature,
@@ -298,7 +298,8 @@ impl SignatureKind for Signature {
ReturnType::Default => OutputKind::Unit,
ReturnType::Type(_, ty) => match **ty {
Type::Path(ref path) => {
if let Some(ident) = path.path.get_ident() {
if let Some(ident) = path.path.segments.first() {
let ident = &ident.ident;
if states.contains(ident) {
return OutputKind::State(ident.clone());
}
@@ -326,13 +327,17 @@ impl SignatureKind for Signature {
fn expand_signature_state(&mut self, info: &StateMachineInfo) {
let fn_out = &mut self.output;
let det_states = &info.det_states;
let non_det_states = &info.non_det_transitions;

if let ReturnType::Type(_, ty) = fn_out {
if let Type::Path(ref mut path) = **ty {
if let Some(ident) = path.path.get_ident() {
if det_states.contains_key(ident) {
let automata_ident = info.get_automaton_ident();
path.path = ::syn::parse_quote!(#automata_ident<#ident>);
} else if let Some(it_enum) = non_det_states.get(ident) {
let generics = &it_enum.generics;
path.path = ::syn::parse_quote!(#ident #generics);
}
}
}

0 comments on commit d08b4e7

Please sign in to comment.