Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor type generation, remove code duplication #352

Merged
merged 52 commits into from
Jan 26, 2022

Conversation

ascjones
Copy link
Contributor

@ascjones ascjones commented Dec 3, 2021

Closes #328. Pure refactoring of the codegen to unify code generating structs and fields, the duplication of which has caused bugs such as: #327.

The main goal of this refactoring was to consolidate the generation of code which produces a set of fields, give some scale_info::Fields. This is required in several different places:

  1. Generation of standalone structs from the type registry
  2. Generation of enum variants, which themselves are represented as a sequence of scale_info::Fields.
  3. Generation of call and event structs, extracted from the variants of the top level Call and Event types.

This refactoring moves most of the core code generation for types which consist of a set of fields to the CompositeDef type. This type is an "intermediate representation" of some set of fields for generation of a struct or an enum variant. A good place to start is to look at it's ToTokens implementation and the field_tokens method which performs the actual codegen of the fields. You will see that all logic to do with adding compact attributes and phantom data fields is consolidated here, where previously it was spread around in various places. See code removed in struct_def.rs and type_def.rs.

I hope that it also improves the modularity of the types codegen to make it easier to understand and maintain. I think there could be more improvements here but this is a step in the right direction.

@dvdplm dvdplm requested review from insipx and jsdw December 10, 2021 13:19
@ascjones ascjones mentioned this pull request Dec 10, 2021
18 tasks
Comment on lines 163 to 166
pub struct CompositeDefFields {
named: bool,
fields: Vec<CompositeDefField>,
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder whether a slightly better fitting type here would be something like:

enum CompositeDefFields {
   Named(Vec<(syn::Ident, CompositeDefField)>),
   Unnamed(Vec<CompositeDefField>)
}

And then remove the name from CompositeDefField. You wouldn't have to check a boolean or handle the option in that case :)

Copy link
Contributor Author

@ascjones ascjones Jan 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, that is similar to the type as it currently is:

pub enum StructDefFields {
Named(Vec<(syn::Ident, TypePath)>),
Unnamed(Vec<TypePath>),
}

It's been a while since I did this but I remember getting into difficulties with this type across it's different usage contexts, hence the relaxing of the strict type.

I will look again at this to see whether it can be improved.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change has now been applied, let me know if it looks good

Copy link
Collaborator

@jsdw jsdw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Barring a small suggestion this looks good to me!

}
}
CompositeDefKind::EnumVariant => {
let fields = self.fields.field_tokens(None, None, false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it would make sense to have separate field_tokens methods for Struct vs Enums, e.g. struct_tokens()/enum_tokens(), to make the signature a bit easier on the eyes here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, let me know if that works now.

use std::collections::HashSet;

#[derive(Clone, Debug, Default)]
pub struct TypeDefParameters {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SOme docs would be good here. Maybe on on the module level as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

if self.unused.is_empty() {
return None
}
let mut params = self.unused.iter().collect::<Vec<_>>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe let mut params = Vec::from_iter(self.unused);?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, actually I have just changed this from HashSet to a BTreeSet for consistent ordering of params, so the conversion to Vec is no longer required.

That said I'm never 100% sure when to use the explicit form e.g. Vec::from_iter. Although probably better than the turbofish in this case, perhaps let mut params: Vec<_> = self.unused.iter().collect(); implicit form is most idiomatic?

Copy link
Contributor

@dvdplm dvdplm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, left a few nitpicks.

@ascjones ascjones merged commit b0004ea into master Jan 26, 2022
@ascjones ascjones deleted the aj-refactor-struct-codegen branch January 26, 2022 09:18
0623forbidden pushed a commit to DEIPworld/substrate-subxt that referenced this pull request Feb 15, 2022
* codegen: fix compact unnamed fields

* Fmt

* codegen: move derives and struct_def to types

* codegen: rename struct_def to composite_def.r

* WIP: deduplicate struct def code

* Fmt

* WIP refactoring composite type codegen duplication

* Fmt

* Fix TokenStream import

* Fix field_tokens ty_path parse error

* Fix call struct generation

* Refactor ty_path()

* Optional derives and move CompactAs derive to composite_def

* Fmt

* Introduce CompositeDefFieldType

* Restore default codec derives

* Extract TypeDefParameters and TypeDefGen construction

* Fmt

* Reset codegen to master

* Introduce CompositeDefFields

* Introduce CompositeDefFields

* Fix up errors

* Fix Box field types

* Fmt

* Fix compact attribute

* Handle no fields case

* Handle no fields with trailing semi

* Fix compact field detection

* Fix generic phantom marker

* Fmt

* Fix generic type parm fields

* Clippy

* Fix up boxed call fields

* Fmt

* Add comments to composite_def.rs

* Fix license headers

* Restore Debug derive in tests

* Fix empty struct codegen test

* Use BTreeSet for type params for ordering

* code review: fix comment

* code review: refactor CompositeDefFields as enum

* Fix empty named fields

* Fix generation of call variant enum structs

* Expand field_tokens into separate methods for struct and enum variants

* Add TypeDefParameters docs

* Fix doc link

* Clippy redundant return
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Refactor duplication for generation of composite fields
3 participants