codegen: Add codegen error (#841)

* codegen: Add codegen error

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Use codegen error instead of aborts

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Remove `proc-macro-error` dependency

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* macro/subxt: Transform codegen error into compile_error!

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* cli: Pretty printing for `CodegenError`

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Update cargo.lock

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* tests: Adjust testing for codegen error

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Fix documentation example

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Export `CodegenError`

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Use collect::<Result<_>, _>()

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* cli: Adjust comment regarding error printing

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Improve error messages

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

---------

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
This commit is contained in:
Alexandru Vasile
2023-03-03 19:57:46 +02:00
committed by GitHub
parent 5320ca9d55
commit a7b45ef1d1
18 changed files with 405 additions and 322 deletions
+12 -13
View File
@@ -2,6 +2,8 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use crate::api::CodegenError;
use super::{
CratePath,
Derives,
@@ -12,7 +14,6 @@ use super::{
TypePath,
};
use proc_macro2::TokenStream;
use proc_macro_error::abort_call_site;
use quote::{
format_ident,
quote,
@@ -52,8 +53,8 @@ impl CompositeDef {
type_gen: &TypeGenerator,
docs: &[String],
crate_path: &CratePath,
) -> Self {
let mut derives = type_gen.type_derives(ty);
) -> Result<Self, CodegenError> {
let mut derives = type_gen.type_derives(ty)?;
let fields: Vec<_> = fields_def.field_types().collect();
if fields.len() == 1 {
@@ -84,7 +85,7 @@ impl CompositeDef {
let name = format_ident!("{}", ident);
let docs_token = Some(quote! { #( #[doc = #docs ] )* });
Self {
Ok(Self {
name,
kind: CompositeDefKind::Struct {
derives,
@@ -93,7 +94,7 @@ impl CompositeDef {
},
fields: fields_def,
docs: docs_token,
}
})
}
/// Construct a definition which will generate code for an `enum` variant.
@@ -183,9 +184,9 @@ impl CompositeDefFields {
fields: &[Field],
parent_type_params: &[TypeParameter],
type_gen: &TypeGenerator,
) -> Self {
) -> Result<Self, CodegenError> {
if fields.is_empty() {
return Self::NoFields
return Ok(Self::NoFields)
}
let mut named_fields = Vec::new();
@@ -209,17 +210,15 @@ impl CompositeDefFields {
}
if !named_fields.is_empty() && !unnamed_fields.is_empty() {
abort_call_site!(
"'{}': Fields should either be all named or all unnamed.",
name,
)
return Err(CodegenError::InvalidFields(name.into()))
}
if !named_fields.is_empty() {
let res = if !named_fields.is_empty() {
Self::Named(named_fields)
} else {
Self::Unnamed(unnamed_fields)
}
};
Ok(res)
}
/// Returns the set of composite fields.
+9 -9
View File
@@ -17,7 +17,6 @@ use proc_macro2::{
Span,
TokenStream,
};
use proc_macro_error::abort_call_site;
use quote::{
quote,
ToTokens,
@@ -30,6 +29,8 @@ use scale_info::{
};
use std::collections::BTreeMap;
use crate::api::CodegenError;
pub use self::{
composite_def::{
CompositeDef,
@@ -91,7 +92,7 @@ impl<'a> TypeGenerator<'a> {
}
/// Generate a module containing all types defined in the supplied type registry.
pub fn generate_types_mod(&self) -> Module {
pub fn generate_types_mod(&self) -> Result<Module, CodegenError> {
let root_mod_ident = &self.types_mod_ident;
let mut root_mod = Module::new(root_mod_ident.clone(), root_mod_ident.clone());
@@ -127,11 +128,11 @@ impl<'a> TypeGenerator<'a> {
self,
&self.crate_path,
self.should_gen_docs,
),
)?,
);
}
root_mod
Ok(root_mod)
}
/// # Panics
@@ -311,12 +312,11 @@ impl<'a> TypeGenerator<'a> {
}
/// Returns the derives to be applied to a generated type.
pub fn type_derives(&self, ty: &Type<PortableForm>) -> Derives {
pub fn type_derives(&self, ty: &Type<PortableForm>) -> Result<Derives, CodegenError> {
let joined_path = ty.path().segments().join("::");
let ty_path: syn::TypePath = syn::parse_str(&joined_path).unwrap_or_else(|e| {
abort_call_site!("'{}' is an invalid type path: {:?}", joined_path, e,)
});
self.derives.resolve(&ty_path)
let ty_path: syn::TypePath = syn::parse_str(&joined_path)
.map_err(|e| CodegenError::InvalidTypePath(joined_path, e))?;
Ok(self.derives.resolve(&ty_path))
}
}
+42 -31
View File
@@ -2,8 +2,10 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use crate::CratePath;
use proc_macro_error::abort;
use crate::{
api::CodegenError,
CratePath,
};
use std::{
borrow::Cow,
collections::HashMap,
@@ -106,39 +108,48 @@ impl TypeSubstitutes {
}
}
pub fn extend(&mut self, elems: impl IntoIterator<Item = (syn::Path, AbsolutePath)>) {
self.substitutes
.extend(elems.into_iter().map(|(path, AbsolutePath(mut with))| {
let Some(syn::PathSegment { arguments: src_path_args, ..}) = path.segments.last() else { abort!(path.span(), "Empty path") };
let Some(syn::PathSegment { arguments: target_path_args, ..}) = with.segments.last_mut() else { abort!(with.span(), "Empty path") };
pub fn extend(
&mut self,
elems: impl IntoIterator<Item = (syn::Path, AbsolutePath)>,
) -> Result<(), CodegenError> {
let to_extend = elems.into_iter().map(|(path, AbsolutePath(mut with))| {
let Some(syn::PathSegment { arguments: src_path_args, ..}) = path.segments.last() else {
return Err(CodegenError::EmptySubstitutePath(path.span()))
};
let Some(syn::PathSegment { arguments: target_path_args, ..}) = with.segments.last_mut() else {
return Err(CodegenError::EmptySubstitutePath(with.span()))
};
let source_args: Vec<_> = type_args(src_path_args).collect();
let source_args: Vec<_> = type_args(src_path_args).collect();
let param_mapping = if source_args.is_empty() {
// If the type parameters on the source type are not specified, then this means that
// the type is either not generic or the user wants to pass through all the parameters
TypeParamMapping::None
} else {
// Describe the mapping in terms of "which source param idx is used for each target param".
// So, for each target param, find the matching source param index.
let mapping = type_args(target_path_args)
.filter_map(|arg|
source_args
.iter()
.position(|&src| src == arg)
.map(|src_idx|
u8::try_from(src_idx).expect("type arguments to be fewer than 256; qed"),
)
).collect();
TypeParamMapping::Specified(mapping)
};
let param_mapping = if source_args.is_empty() {
// If the type parameters on the source type are not specified, then this means that
// the type is either not generic or the user wants to pass through all the parameters
TypeParamMapping::None
} else {
// Describe the mapping in terms of "which source param idx is used for each target param".
// So, for each target param, find the matching source param index.
let mapping = type_args(target_path_args)
.filter_map(|arg|
source_args
.iter()
.position(|&src| src == arg)
.map(|src_idx|
u8::try_from(src_idx).expect("type arguments to be fewer than 256; qed"),
)
).collect();
TypeParamMapping::Specified(mapping)
};
// NOTE: Params are late bound and held separately, so clear them
// here to not mess pretty printing this path and params together
*target_path_args = syn::PathArguments::None;
// NOTE: Params are late bound and held separately, so clear them
// here to not mess pretty printing this path and params together
*target_path_args = syn::PathArguments::None;
(PathSegments::from(&path), Substitute { path: with, param_mapping })
}));
Ok((PathSegments::from(&path), Substitute { path: with, param_mapping }))
}).collect::<Result<Vec<_>, _>>()?;
self.substitutes.extend(to_extend);
Ok(())
}
/// Given a source type path, return a substituted type path if a substitution is defined.
+20 -20
View File
@@ -48,7 +48,7 @@ fn generate_struct_with_primitives() {
crate_path,
true,
);
let types = type_gen.generate_types_mod();
let types = type_gen.generate_types_mod().expect("Valid type mod; qed");
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
@@ -97,7 +97,7 @@ fn generate_struct_with_a_struct_field() {
crate_path,
true,
);
let types = type_gen.generate_types_mod();
let types = type_gen.generate_types_mod().expect("Valid type mod; qed");
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
@@ -145,7 +145,7 @@ fn generate_tuple_struct() {
crate_path,
true,
);
let types = type_gen.generate_types_mod();
let types = type_gen.generate_types_mod().expect("Valid type mod; qed");
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
@@ -230,7 +230,7 @@ fn derive_compact_as_for_uint_wrapper_structs() {
crate_path,
true,
);
let types = type_gen.generate_types_mod();
let types = type_gen.generate_types_mod().expect("Valid type mod; qed");
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
@@ -297,7 +297,7 @@ fn generate_enum() {
crate_path,
true,
);
let types = type_gen.generate_types_mod();
let types = type_gen.generate_types_mod().expect("Valid type mod; qed");
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
@@ -358,7 +358,7 @@ fn compact_fields() {
crate_path,
true,
);
let types = type_gen.generate_types_mod();
let types = type_gen.generate_types_mod().expect("Valid type mod; qed");
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
@@ -417,7 +417,7 @@ fn compact_generic_parameter() {
crate_path,
true,
);
let types = type_gen.generate_types_mod();
let types = type_gen.generate_types_mod().expect("Valid type mod; qed");
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
@@ -461,7 +461,7 @@ fn generate_array_field() {
crate_path,
true,
);
let types = type_gen.generate_types_mod();
let types = type_gen.generate_types_mod().expect("Valid type mod; qed");
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
@@ -501,7 +501,7 @@ fn option_fields() {
crate_path,
true,
);
let types = type_gen.generate_types_mod();
let types = type_gen.generate_types_mod().expect("Valid type mod; qed");
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
@@ -544,7 +544,7 @@ fn box_fields_struct() {
crate_path,
true,
);
let types = type_gen.generate_types_mod();
let types = type_gen.generate_types_mod().expect("Valid type mod; qed");
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
@@ -587,7 +587,7 @@ fn box_fields_enum() {
crate_path,
true,
);
let types = type_gen.generate_types_mod();
let types = type_gen.generate_types_mod().expect("Valid type mod; qed");
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
@@ -630,7 +630,7 @@ fn range_fields() {
crate_path,
true,
);
let types = type_gen.generate_types_mod();
let types = type_gen.generate_types_mod().expect("Valid type mod; qed");
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
@@ -677,7 +677,7 @@ fn generics() {
crate_path,
true,
);
let types = type_gen.generate_types_mod();
let types = type_gen.generate_types_mod().expect("Valid type mod; qed");
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
@@ -728,7 +728,7 @@ fn generics_nested() {
crate_path,
true,
);
let types = type_gen.generate_types_mod();
let types = type_gen.generate_types_mod().expect("Valid type mod; qed");
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
@@ -782,7 +782,7 @@ fn generate_bitvec() {
crate_path,
true,
);
let types = type_gen.generate_types_mod();
let types = type_gen.generate_types_mod().expect("Valid type mod; qed");
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
@@ -838,7 +838,7 @@ fn generics_with_alias_adds_phantom_data_marker() {
crate_path,
true,
);
let types = type_gen.generate_types_mod();
let types = type_gen.generate_types_mod().expect("Valid type mod; qed");
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
@@ -901,7 +901,7 @@ fn modules() {
crate_path,
true,
);
let types = type_gen.generate_types_mod();
let types = type_gen.generate_types_mod().expect("Valid type mod; qed");
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
@@ -961,7 +961,7 @@ fn dont_force_struct_names_camel_case() {
crate_path,
true,
);
let types = type_gen.generate_types_mod();
let types = type_gen.generate_types_mod().expect("Valid type mod; qed");
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
@@ -1005,7 +1005,7 @@ fn apply_user_defined_derives_for_all_types() {
crate_path,
true,
);
let types = type_gen.generate_types_mod();
let types = type_gen.generate_types_mod().expect("Valid type mod; qed");
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
@@ -1073,7 +1073,7 @@ fn apply_user_defined_derives_for_specific_types() {
crate_path,
true,
);
let types = type_gen.generate_types_mod();
let types = type_gen.generate_types_mod().expect("Valid type mod; qed");
let tests_mod = get_mod(&types, MOD_PATH).unwrap();
assert_eq!(
+12 -9
View File
@@ -2,6 +2,8 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use crate::api::CodegenError;
use super::{
CompositeDef,
CompositeDefFields,
@@ -46,8 +48,8 @@ impl TypeDefGen {
type_gen: &TypeGenerator,
crate_path: &CratePath,
should_gen_docs: bool,
) -> Self {
let derives = type_gen.type_derives(ty);
) -> Result<Self, CodegenError> {
let derives = type_gen.type_derives(ty)?;
let type_params = ty
.type_params()
@@ -78,7 +80,7 @@ impl TypeDefGen {
composite.fields(),
type_params.params(),
type_gen,
);
)?;
type_params.update_unused(fields.field_types());
let docs = should_gen_docs.then_some(ty.docs()).unwrap_or_default();
let composite_def = CompositeDef::struct_def(
@@ -90,11 +92,12 @@ impl TypeDefGen {
type_gen,
docs,
crate_path,
);
)?;
TypeDefGenKind::Struct(composite_def)
}
TypeDef::Variant(variant) => {
let type_name = ty.path().ident().expect("variants should have a name");
let variants = variant
.variants()
.iter()
@@ -104,15 +107,15 @@ impl TypeDefGen {
v.fields(),
type_params.params(),
type_gen,
);
)?;
type_params.update_unused(fields.field_types());
let docs =
should_gen_docs.then_some(v.docs()).unwrap_or_default();
let variant_def =
CompositeDef::enum_variant_def(v.name(), fields, docs);
(v.index(), variant_def)
Ok((v.index(), variant_def))
})
.collect();
.collect::<Result<Vec<_>, CodegenError>>()?;
TypeDefGenKind::Enum(type_name, variants)
}
@@ -124,12 +127,12 @@ impl TypeDefGen {
.then_some(quote! { #( #[doc = #docs ] )* })
.unwrap_or_default();
Self {
Ok(Self {
type_params,
derives,
ty_kind,
ty_docs,
}
})
}
}