-
Notifications
You must be signed in to change notification settings - Fork 579
Support custom recursion limits at build time #785
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
base: master
Are you sure you want to change the base?
Conversation
b2bb84c to
9a50224
Compare
|
thoughts @LucioFranco @nrc @danburkert? |
src/lib.rs
Outdated
| // See `encoding::DecodeContext` for more info. | ||
| // 100 is the default recursion limit in the C++ implementation. | ||
| #[cfg(not(feature = "no-recursion-limit"))] | ||
| const RECURSION_LIMIT: u32 = 100; |
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.
Since it sounds like this will never change, I opted to inline it everywhere it's used. That allows documentation to say explicitly that the default recursion limit is 100 instead of requiring that users look up this constant.
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 actually kinda like having this as a constant. I wonder if we could just make a constants module that contains just this one. And we can then have all the deep dive docs on the recursion implementation there and then we just need to link to there from the lib doc page. I feel like that would make it easier to maintain down the line and centralize it a bit.
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'm not sure if a constants module adds much. If you still feel a constant is necessary, I'd lean towards simply adding back the constant here in lib.rs
|
Here's a downstream PR using this feature: pganalyze/pg_query.rs#17 Note that to prevent a stack overflow from the recursion, I had to increase the stack size in a separate thread. I wonder if that should be documented here. |
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.
Overall LGTM! Some small suggestions and we can get this merged. Thanks for the patience I went on vacation in between :)
|
@LucioFranco this should be ready for re-review |
|
@LucioFranco ping. happy to make any other changes if needed |
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.
Sorry for the super long delay on the review here and thank you for the ping (I generally don't mind them if you're waiting for a review). I left a few comments that I would like to see changed but they are pretty minor and I think once those are good we are good to merge. Thank you for pushing through on this!
prost-derive/src/lib.rs
Outdated
| let recursion_limit: u32 = if let Some(attr) = input | ||
| .attrs | ||
| .iter() | ||
| .find(|attr| attr.path.is_ident("RecursionLimit")) |
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 wonder if we should make this one camel case like serde does https://serde.rs/container-attrs.html
so #[prost(recursion_limit = 5)] etc
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'm having trouble getting a bespoke attribute parser working. Would it be okay to pull in darling as a dependency? https://crates.io/crates/darling
Another option is to skip proper attribute parsing and only handle the single attribute we have for now.
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.
Two years later and I finally got around to this. The code now uses #[prost(recursion_limit = 5)]
src/encoding.rs
Outdated
| /// The recursion limit is defined by `RECURSION_LIMIT` and cannot be | ||
| /// customized. The recursion limit can be ignored by building the Prost | ||
| /// crate with the `no-recursion-limit` feature. | ||
| /// It defaults to 100 and can be changed using `prost_build::recursion_limit`, |
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.
So because this is hidden this doc won't actually be readable. So we need to make sure this is documented at the lib level of prost.
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've added a mention to the README
src/lib.rs
Outdated
| // See `encoding::DecodeContext` for more info. | ||
| // 100 is the default recursion limit in the C++ implementation. | ||
| #[cfg(not(feature = "no-recursion-limit"))] | ||
| const RECURSION_LIMIT: u32 = 100; |
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 actually kinda like having this as a constant. I wonder if we could just make a constants module that contains just this one. And we can then have all the deep dive docs on the recursion implementation there and then we just need to link to there from the lib doc page. I feel like that would make it easier to maintain down the line and centralize it a bit.
538a0ea to
08caf43
Compare
08caf43 to
7fc6689
Compare
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.
Thank you for coming back to this after two years. I see why this is useful and this is a smart solution.
I am a bit concerned whether this could be a breaking change. I would like to see some prove that it is not.
README.md
Outdated
| - `derive`: Enable integration with `prost-derive`. Disable this feature to reduce compile times. This feature is enabled by default. | ||
| - `prost-derive`: Deprecated. Alias for `derive` feature. | ||
| - `no-recursion-limit`: Disable the recursion limit. The recursion limit is 100 and cannot be customized. | ||
| - `no-recursion-limit`: Disable the recursion limit. The recursion limit is 100, and can be changed with `prost_build::recursion_limit`. |
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.
That should be prost_build::Config::recursion_limit, right?
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.
Good catch
|
|
||
| /// Get the items belonging to the 'prost' list attribute, e.g. `#[prost(foo, bar="baz")]`. | ||
| fn prost_attrs(attrs: Vec<Attribute>) -> Result<Vec<Meta>, Error> { | ||
| pub(super) fn prost_attrs(attrs: Vec<Attribute>) -> Result<Vec<Meta>, 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.
Just to verify: this doesn't make the function visible outside this crate, right?
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.
Right: pub(super) makes an item visible to the parent module
| #(#clear;)* | ||
| } | ||
|
|
||
| fn recursion_limit() -> u32 { |
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.
Is this a breaking change for messages with a field named recursion_limit?
I think for enum fields, an accessor is created. However, this function is in a trait, so not directly accessible.
I would like to see a test case for that.
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'm not sure I follow: how could this possibly affect a field named recursion_limit? This is implemented on the Message trait, not on the struct itself.
prost/src/encoding.rs
Outdated
| /// or it can be disabled entirely using the `no-recursion-limit` feature. | ||
| #[cfg(not(feature = "no-recursion-limit"))] | ||
| recurse_count: u32, | ||
| #[doc(hidden)] |
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.
Why is this marked as doc hidden?
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.
@LucioFranco previously suggested that it should be doc(hidden) because the field was changed to public. I assume a previous version of this PR needed it to be public but it seems like it's no longer needed. I'll revert this field back to private.
| /// The recursion limit for decoding protobuf messages. | ||
| /// | ||
| /// Defaults to 100. Can be customized in your build.rs or by using the no-recursion-limit crate feature. | ||
| fn recursion_limit() -> u32 |
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.
Is this a breaking change? I feel like it is not, because you add a default implementation. However rust rules for breaking change are not always logical. Could you verify this with cargo-semver-checks?
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.
My understanding is that it's not a breaking change because it has a default implementation.
I tried running cargo-semver-checks locally and it highlighted #1247 as being a breaking change, but doesn't mention anything about this PR:
--- failure feature_missing: package feature removed or renamed ---
Description:
A feature has been removed from this package's Cargo.toml. This will break downstream crates which enable that feature.
ref: https://doc.rust-lang.org/cargo/reference/semver.html#cargo-feature-remove
impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.41.0/src/lints/feature_missing.ron
Failed in:
feature prost-derive in the package's Cargo.toml
Summary semver requires new major version: 1 major and 0 minor checks failed
Maybe this check should run on CI?
This PR allows users to set a custom recursion limit for specific protobuf structures at build time: