-
Notifications
You must be signed in to change notification settings - Fork 123
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
Initial derive-Error proc-macro implementation #103
Initial derive-Error proc-macro implementation #103
Conversation
Thank you for opening the WIP PR. I do still want you to use The main reason I want this to use #[derive(Error)]
struct WithoutSource(#[error(not(source))] i32); #[derive(Error)]
#[error(not(source))]
struct WithoutSource(i32); Also, the |
When using |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ffuugoo ☝️
Another nice thing that the Error derive would get as a free benefit from State is this (once it's implemented): #102 |
@ffuugoo FYI, I've implemented checking of supported attribute parameters for |
This is great. I've worked on moving derive-Error to using |
So, I've worked on on moving derive-Error to using So, as far as I understand, the way
The problem is, if we want to implement generic So, I see two ways going from here:
|
@ffuugoo Awesome that you're working so much on this! It's a shame that the current MetaInfo approach is not flexible enough, but I already expected to run into it at some point. I mainly wanted to keep it simple while it was not needed yet. I think option 2 would be most desirable, the |
As far as I understand embedding it should also not break existing code. |
I also really don't like the whole This way we:
The only real downside I see, is that there is no trivial way to "default-merge" a
|
Didn't think about it. Seems simple/good enough. I'll see how far I can get with this. (: |
I'm definetly not sold on the current design of MetaInfo/FullMetaInfo. The main reason it is like it is now is because it was simple to write/understand and did the job. If embedding FullMetaInfo is good enough for you I think doing that is best for now, since it seems another simple solution. In the future I definitely think rethinking the MetaInfo/FullMetaInfo approach is a good idea. Things to keep in mind though:
|
#[derive(Copy, Clone, <stuff required for HashMap key>)]
enum ValidAttr {
Source,
Backtrace,
};
impl ValidAttr {
fn str(&self) -> &'static str {
match self {
Self::Source => "source",
Self::Backtrace => "backtrace",
}
}
}
const VALID_ATTRS: &[ValidAttr] = [
ValidAttr::Source,
ValidAttr::Backtrace,
];
fn test(parsed_attrs: &mut HashMap<ValidAttr, bool>, path: &Path) {
let attr = VALID_ATTRS.find(|attr| path.is_ident(attr.str()));
parsed_attrs.insert(attr, true);
} Kinda the same amount of work, but very trivial and well scoped. |
I see you added some more commits. I'll try to take a look at them soon-ish. One thing that's important, I want to release this with some preliminary |
@JelteF I suggest to implement |
@tyranron I think that indeed makes sense, I've changed this PR to merge into the @ffuugoo For clarity, is this one still just a WIP PR? Or is it ready for a full review. |
@JelteF still WIP. We will remove the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left some preliminary comments, mostly with ideas to better utilize State
.
src/error.rs
Outdated
where | ||
P: Fn(&syn::Field, usize) -> bool, | ||
{ | ||
let fields = state.enabled_fields_data(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use unpacking here to make the rest of the code clearer.
src/error.rs
Outdated
info, | ||
)?, | ||
|
||
None => process_if_valid_default_field_for_attr( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's split this into two loops. First checking all fields for explicit setting of the source
and only if that's not done try to infer it. I think the flow is a bit clearer that way and some of the error cases below disappear.
src/error.rs
Outdated
} | ||
|
||
impl<'a> ParsedField<'a> { | ||
fn render_as_struct_field_ident(&self) -> TokenStream { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
usage of this function can be replaced by multi_field_data.members[self.index]
tests/error.rs
Outdated
fn named_implicit_source() { | ||
#[derive(Default, Debug, Display, Error)] | ||
#[display(fmt = "")] | ||
struct S<SS, T> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This type parameter is quite confusing since the above error type is also called SS
. Let's change it to E
(also for the types below).
tests/error.rs
Outdated
|
||
#[derive(Debug, Display, Error)] | ||
#[display(fmt = "")] | ||
enum E<S, T> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also here, let's rename this S
type parameter such that it doesn't shadow the actual type.
src/error.rs
Outdated
} | ||
} | ||
|
||
fn render_as_enum_variant_struct_match_arm_tail(&self, len: usize) -> TokenStream { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This an the next function can be replaced by a call to MultiFieldData.initializer
.
Something like this:
let bindings: Vec<_> = (0..len).map(|index| {
if index == self.index {
quote!(source)
} else {
quote!(_)
}
}).collect();
multi_field_data.initializers(&bindings)
I think this also allows you to remove derive_type
from ParsedFields
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just added multi_field_data.matcher
, now you can do:
multi_field_data.matcher(&[self.index], &[quote!(source)])
@JelteF Should have fixed all of your comments now. |
@tyranron Take a look as well, pls. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
enum_: vec!["ignore"], | ||
struct_: vec!["ignore"], | ||
variant: vec!["ignore"], | ||
field: vec!["ignore", "source"], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no tests at all for the ignore
attribute. Let's either add tests or disallow the ignore
attribute if it doesn't make sense for error (which I think is the case).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does not make much sense, but it does not contradict the design as well. Added tests for possible ignore
cases.
&generics, | ||
quote! { | ||
where | ||
#(#bounds: ::std::fmt::Debug + ::std::fmt::Display + ::std::error::Error + 'static),* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are the Debug and Display bounds necessary? Doesn't Error
already require those?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems so. Tests fail to compile for me if I remove these extra bounds.
[[test]] | ||
name = "error" | ||
path = "tests/error.rs" | ||
required-features = ["error"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should have failed in CI
, since the test actually requires the display
feature as well for the Display
derives. I've updated master with a small change to the testing so that this should now fail.
Let's rename this test to error_full
and add display
to required-features.
Apart from that let's create a small error
test, with a some simple Error
derives that have a simple Display
implementation written out so that we test that the error
feature works.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed display
dependency for tests/error
altogether.
src/parsing.rs
Outdated
@@ -23,7 +23,7 @@ pub type ParseResult<T> = Result<T, ParseError>; | |||
impl ::std::fmt::Display for ParseError { | |||
fn fmt( | |||
&self, | |||
fmt: &mut ::std::fmt::Formatter<'_>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes to this file seem unnecessary. Let's undo those.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
match meta { | ||
Meta::List(list) if list.path.is_ident("not") => { | ||
if value { | ||
parse_punctuated_nested_meta( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because this is called recursively it allows #[error(not(not(source))]
, which is confusing as it's the same as #[error(not(source))]
. Let's call another function here, that doesn't allow any Meta::List
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not. if value
conditional only allows for a single top-level "not" or fails with an error otherwise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see, can you add a comment explaining that check then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Co-Authored-By: Jelte Fennema <github-tech@jeltef.nl>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me now, but there are some merge conflicts with the latest master. If you solve those we can merge it into the error
branch. FYI I'll probably only have time to review the changes for Backtrace
somewhere in December.
This is now released in version 0.99.5 |
First part of #92.
Not based on
utils::State
and supports onlysource
attribute/method, so far.Attribute syntax is the one suggested by @tyranron. Lots of examples in
tests/error.rs
.