Skip to content

Commit

Permalink
Fix missing generics on builder entry point where concrete types are …
Browse files Browse the repository at this point in the history
…specified on the impl. (#62)
  • Loading branch information
BrynCooke authored Jun 9, 2022
1 parent e36f336 commit d406846
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 3 deletions.
18 changes: 17 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,22 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## 0.3.1 - Unreleased

[#60](https://github.com/BrynCooke/buildstructor/issues/55)
Fix impl with concrete types in generics.

```
#[buildstructor]
impl Foo<usize> {
#[builder]
fn bound_new(simple: usize) -> Foo<usize> {
Self { simple }
}
}
```
Previously the generated builder method was not including concrete generic type. In this case `usize`.

## 0.3.0 - 2022-05-23

[#55](https://github.com/BrynCooke/buildstructor/issues/55)
Expand All @@ -12,7 +28,7 @@ Lifetimes are supported now.
[#52](https://github.com/BrynCooke/buildstructor/issues/52)
Add docs to generated builder.
In addition, a type alias is introduced for the initial builder type so that:
1. the docs looks nice
1. the docs look nice
2. the builder can be passed to a function (although this is of limited real world use).

[#4](https://github.com/BrynCooke/buildstructor/issues/4)
Expand Down
4 changes: 3 additions & 1 deletion src/buildstructor/analyze.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use quote::format_ident;
use syn::spanned::Spanned;
use syn::{
Attribute, FnArg, Generics, Ident, ImplItem, ImplItemMethod, ItemImpl, Lit, Meta,
MetaNameValue, NestedMeta, Result, ReturnType, Visibility,
MetaNameValue, NestedMeta, Result, ReturnType, Type, Visibility,
};

use crate::parse::Ast;
Expand All @@ -19,6 +19,7 @@ pub struct BuilderModel {
pub vis: Visibility,
pub config: BuilderConfig,
pub attributes: Vec<Attribute>,
pub self_ty: Box<Type>,
}

#[derive(Debug, Clone, Default)]
Expand Down Expand Up @@ -129,6 +130,7 @@ pub fn analyze(legacy_default_builders: bool, ast: &Ast) -> Result<Vec<Result<Bu
Ok(BuilderModel {
impl_name: ident.clone(),
impl_generics: ast.item.generics.clone(),
self_ty: ast.item.self_ty.clone(),
delegate_name: builder.sig.ident.clone(),
delegate_generics: builder.sig.generics.clone(),
delegate_args: builder.sig.inputs.clone().into_iter().collect(),
Expand Down
9 changes: 8 additions & 1 deletion src/buildstructor/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ pub fn codegen(ir: Ir) -> Result<TokenStream> {
ir.impl_name,
);

let self_ty = &ir.self_ty;

let (impl_generics, ty_generics, where_clause) = &ir.impl_generics.split_for_impl();

let param_generics = ir.param_generics();
Expand Down Expand Up @@ -148,7 +150,7 @@ pub fn codegen(ir: Ir) -> Result<TokenStream> {
let type_doc = "Autogenerated by buildstructor";

Ok(quote! {
impl #impl_generics #target_name #ty_generics #where_clause {
impl #impl_generics #self_ty #where_clause {
#(#doc)*
#vis fn #builder_entry #method_generics(#receiver) -> #builder_alias_name #builder_init_generic_args {
#module_name::new(#builder_receiver)
Expand Down Expand Up @@ -635,4 +637,9 @@ mod tests {
fn lifetime() {
assert_codegen!(lifetime_test_case());
}

#[test]
fn specialization() {
assert_codegen!(specialization_test_case());
}
}
2 changes: 2 additions & 0 deletions src/buildstructor/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ pub struct Ir {
pub receiver: Option<Receiver>,
pub doc: Vec<Attribute>,
pub implicit_lifetime: bool,
pub self_ty: Box<Type>,
}

pub struct BuilderField {
Expand Down Expand Up @@ -65,6 +66,7 @@ pub fn lower(model: BuilderModel) -> Result<Ir> {
),
impl_name: model.impl_name.clone(),
impl_generics: model.impl_generics.clone(),
self_ty: model.self_ty.clone(),
delegate_name: model.delegate_name.clone(),
delegate_generics: model.delegate_generics.clone(),
builder_name: format_ident!("__{}Builder", model.impl_name),
Expand Down
12 changes: 12 additions & 0 deletions src/buildstructor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,4 +313,16 @@ mod tests {
}
)
}

pub fn specialization_test_case() -> Ast {
parse_quote!(
#[buildstructor]
impl Foo<usize> {
#[builder]
fn bound_new(simple: usize) -> Foo<usize> {
Self { simple }
}
}
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
source: src/buildstructor/codegen.rs
assertion_line: 641
expression: output
---
impl Foo<usize> {
fn bound_builder() -> BoundNewFooBuilder {
__foo_bound_new_builder::new()
}
}
///Autogenerated by buildstructor
type BoundNewFooBuilder = __foo_bound_new_builder::__FooBuilder<
(__foo_bound_new_builder::__Required<usize>,),
>;
mod __foo_bound_new_builder {
use super::*;
#[inline(always)]
pub(super) fn new() -> __FooBuilder<(__foo_bound_new_builder::__Required<usize>,)> {
__FooBuilder {
fields: (__required(),),
_phantom: core::default::Default::default(),
}
}
pub(super) struct __Required<T> {
_phantom: std::marker::PhantomData<T>,
}
pub(super) struct __Optional<T> {
lazy: Option<T>,
}
pub(super) struct __Set<T> {
value: T,
}
#[inline(always)]
fn __set<T>(value: T) -> __Set<T> {
__Set { value }
}
#[inline(always)]
fn __required<T>() -> __Required<T> {
__Required::<T> {
_phantom: core::default::Default::default(),
}
}
#[inline(always)]
fn __optional<T>() -> __Optional<T> {
__Optional::<T> { lazy: None }
}
impl<T: Default> From<__Optional<T>> for __Set<T> {
#[inline(always)]
fn from(o: __Optional<T>) -> Self {
__Set {
value: o.lazy.unwrap_or_default(),
}
}
}
pub(super) struct __FooBuilder<__P> {
fields: __P,
_phantom: core::marker::PhantomData<()>,
}
impl __FooBuilder<(__Required<usize>,)> {
#[inline(always)]
pub(super) fn simple(self, simple: usize) -> __FooBuilder<(__Set<usize>,)> {
let simple = simple;
__FooBuilder {
fields: (__set(simple),),
_phantom: core::default::Default::default(),
}
}
}
impl<__P0: Into<__Set<usize>>> __FooBuilder<(__P0,)> {
#[inline(always)]
pub(super) fn build(self) -> Foo<usize> {
Foo::bound_new(self.fields.0.into().value)
}
}
}

9 changes: 9 additions & 0 deletions tests/buildstructor/pass/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,17 @@ impl<T: std::fmt::Debug> Foo<T> {
}
}

#[buildstructor]
impl Foo<usize> {
#[builder]
fn bound3_new(simple: usize) -> Foo<usize> {
Self { simple }
}
}

fn main() {
let _ = Foo::builder().simple(3).build();
let _ = Foo::bound1_builder().simple(3).build();
let _ = Foo::bound2_builder().simple(3).build();
let _ = Foo::bound3_builder().simple(3).build();
}

0 comments on commit d406846

Please sign in to comment.