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

Rewrite AsChangeset using syn 0.12 #1512

Merged
merged 2 commits into from
Jan 31, 2018
Merged

Rewrite AsChangeset using syn 0.12 #1512

merged 2 commits into from
Jan 31, 2018

Conversation

sgrif
Copy link
Member

@sgrif sgrif commented Jan 24, 2018

In order to update to syn 0.12, we're going to have to rewrite a lot of
diesel_derives. A lot of code is incompatible with no obvious
migration path. I also want to take this as an opportunity to do a lot
of long needed refactoring to this library.

Despite continuous assertions that outputting compile_error! somehow
makes our error messages significantly better (it really doesn't, it
just avoids "proc-macro panicked") -- I am panicking in error cases
here. One of the benefits of syn 0.12 is that it retains span
information, which we can use on nightly to output errors with real
diagnostics. I haven't made that jump here quite yet, but I plan to in a
followup PR.

Since this is the first derive moved over to the new crate, it includes
much more code than PRs for the rest will.

@sgrif sgrif requested review from weiznich and a team January 24, 2018 19:59
@sgrif
Copy link
Member Author

sgrif commented Jan 24, 2018

/cc @dtolnay mind taking a look at this?

}
}

pub fn camel_to_snake(name: &str) -> String {
Copy link
Member Author

Choose a reason for hiding this comment

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

This function and infer_table_name were both copied over from the old codebase. I think I want to deprecate the inferred table name behavior (I want to hold off on that until everything is moved over though)

@sgrif sgrif force-pushed the sg-rewrite-as-changeset branch 2 times, most recently from 21e8811 to 10ee483 Compare January 24, 2018 20:30
In order to update to syn 0.12, we're going to have to rewrite a lot of
`diesel_derives`. A lot of code is incompatible with no obvious
migration path. I also want to take this as an opportunity to do a lot
of long needed refactoring to this library.

Despite continuous assertions that outputting `compile_error!` somehow
makes our error messages significantly better (it really doesn't, it
just avoids "proc-macro panicked") -- I am panicking in error cases
here. One of the benefits of syn 0.12 is that it retains span
information, which we can use on nightly to output errors with real
diagnostics. I haven't made that jump here quite yet, but I plan to in a
followup PR.

Since this is the first derive moved over to the new crate, it includes
much more code than PRs for the rest will.
@sgrif
Copy link
Member Author

sgrif commented Jan 24, 2018

After playing around with Diagnostic, I realize that I need to structure this to return errors, not panic.

@sgrif sgrif closed this Jan 24, 2018
@sgrif sgrif reopened this Jan 24, 2018
@sgrif
Copy link
Member Author

sgrif commented Jan 24, 2018

I guess I'll restructure to not-panic separately..

Copy link

@dtolnay dtolnay left a comment

Choose a reason for hiding this comment

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

Looks like you are on the right track. This is much neater code than serde_derive! Nicely done.

}
}

pub fn table_name(&self) -> Cow<syn::Ident> {
Copy link

Choose a reason for hiding this comment

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

Ident is cheaply copyable so I would not bother with Cow here.

pub fn table_name(&self) -> syn::Ident {
    self.table_name_from_attribute
        .unwrap_or_else(|| infer_table_name(self.name))
}

.unwrap_or_else(|_| vec!["id".into()]);
let fields = fields_from_item_data(&item.data);
Self {
name: item.ident.clone(),
Copy link

Choose a reason for hiding this comment

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

This can be item.ident without clone().

.ok()
.map(|m| m.expect_ident_value());
let identifier = match field.ident {
Some(ref x) => Identifier::Named(x.clone()),
Copy link

Choose a reason for hiding this comment

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

Some(x) => Identifier::Named(x)


let (_, ty_generics, where_clause) = item.generics.split_for_impl();
let mut impl_generics = item.generics.clone();
impl_generics.params.push(parse_quote!('update));
Copy link

Choose a reason for hiding this comment

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

You are going to want another split_for_impl after this line.

let (impl_generics, _, _) = impl_generics.split_for_impl();

Otherwise it does not work for types that have generic type parameter defaults.

#[derive(AsChangeset)]
#[table_name = "users"]
#[primary_key(name)]
struct Changes2<T = String> {
    name: T,
    hair_color: String,
}
error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions.
   --> tests/update.rs:390:14
    |
390 |     #[derive(AsChangeset)]
    |              ^^^^^^^^^^^
    |
    = note: #[deny(invalid_type_param_default)] on by default
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #36887 <https://github.com/rust-lang/rust/issues/36887>

Copy link
Member Author

Choose a reason for hiding this comment

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

This code doesn't currently work with generic structs, but good catch.

repository = "https://github.com/diesel-rs/diesel/tree/master/diesel_derives"

[dependencies]
syn = { version = "0.12.0", features = ["full"] }
Copy link
Member

Choose a reason for hiding this comment

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

Do we really need the full feature here?
As far as I can see the default features should be sufficient for diesel.

Copy link
Member Author

Choose a reason for hiding this comment

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

Our test suite fails without the full feature.

let column_name_from_attribute = MetaItem::with_name(&field.attrs, "column_name")
.ok()
.map(|m| m.expect_ident_value());
let identifier = match field.ident {
Copy link
Member

Choose a reason for hiding this comment

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

Wouldn't

field.ident.map(Identifier::Named).unwrap_or_else(|| Identifier::Unnamed(index.to_string().into())

be a bit more idiomatic?

}
}

pub fn column_name(&self) -> &syn::Ident {
Copy link
Member

Choose a reason for hiding this comment

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

Ident seems to be Copy now. Maybe return no reference than?

Also fixes a potential future issue with generic structs. Without the
`.split_for_impl` line, we would fail on any type parameter with
defaults (right now we don't support generic structs at all here, but
this will be needed if we ever do)
@sgrif sgrif merged commit 06bf3b1 into master Jan 31, 2018
@sgrif sgrif deleted the sg-rewrite-as-changeset branch January 31, 2018 19:15
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.

None yet

3 participants