-
-
Notifications
You must be signed in to change notification settings - Fork 80
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
#[doc(hidden)]
result in false positives
#120
Comments
#[doc(hidden)]
result in false positives
Thanks for reporting. I don't think it's fundamentally impossible to fix. We just need more data from rustdoc and then to tweak some queries to act on that data appropriately. Action items:
Related to #58 since users may occasionally want to explicitly include a |
I don't even remember why we do this. Can we get rid of private items? |
TL;DR: We can, but I'd wait a bit first just to be safe. It's because of a bug in The bug has since been fixed, but I don't remember whether the fix is before or after the last rustdoc JSON format was introduced. If it was after the last format change, then removing private items is equivalent to tightening the minimum nightly version we support, which might be a bit more trouble than it's worth right now. The next time we need a newer nightly bound, we should remove it then. |
Update: We could work around this requirement if need be, but that additional effort at the moment is perhaps better spent elsewhere. |
This is reported directly in |
As such, it seems wrong to me that changes in hidden items are detected as "breaking semver". As such, I disagree with this conclusion:
If you want to communicate that a previously existing symbol should no longer be used,
If on the other hand the presence of the symbol is a deal-breaking bug, the symbol should be removed, and the next semver-incompatible version should be released.
Some popular crates follow this understanding, for example syn. This view is further backed by the Rust API guidelines:
|
Another nuance, posted without prejudice: some If we stopped using It's clear that this issue is both important and not straightforward to solve. More research is needed to come up with a good solution. |
If there are different usages of PyO3, for example, has a lot of It might be good enough for PyO3's case to have an option |
Thanks for the suggestion. I'd love to flesh this out a bit more and understand how a few edge cases here should behave:
Edge cases like these are tricky and difficult to reason about. I'd love your help in getting them right! |
At least for our use case, the re-export itself is most likely an error and we would benefit from it being reported, independent of any changes. As a corollary, changes could be reported as well.
I think just publically exposing these items outside of the crate and allowing to list them could be reported as a potential semver hazard. Thereby, I would say changes can be reported as this is strictly less of a requirement.
Definitely because it affects types we expect our uses to access. |
Maybe as proposal for a mechanistic definition: Allow listing a path prefix means that these items are completely removed before any checks are performed. Anything publically visible referencing them (and not removed itself in the previous step) is an error by itself. |
Ultimately this will be some form of query tweak equivalent to a mechanistic definition, since all the lints are written as queries. Two things we have to be careful of, though:
Probing more edge cases, imagine we have a type defined in a semver-exempt module and not re-exported elsewhere.
I'm worried about situations like macro code producing something the user is expected to use or observe directly in some limited fashion. |
An observation I made recently gave me an idea for solving this problem.
Consider the following situation: #[doc(hidden)]
pub mod inner {
pub struct Foo;
pub struct Bar;
}
pub use inner::Foo; Here, We already have infrastructure in place for computing all possible import paths. This was a hard problem to solve, but we've solved it — and with a slight extension we can reuse that solution to solve ProposalI propose we add a new edge to the Then we can adjust queries to use
We must consider two more factors: deprecations, and transitions from non-hidden to hidden and vice versa. Handling deprecationsWhen maintainers deprecate items, they also make them However, such hidden + deprecated items may still be in use by crates in the ecosystem, so the APIs should still be considered non-hidden for semver-checking purposes. Perhaps we should implement this along the lines of "deprecation undoes Handling transitions between hidden and non-hidden statusItems that were initially added as A hidden item becoming non-hidden should be a minor change, as if the item was just newly added. A non-hidden item becoming hidden without being deprecated must be reported as major+breaking. Here's why: say we don't do this, then we can have a situation where v1.0 adds a non-hidden pub API, v1.1 hides part of that pub API, then v1.2 deletes the hidden portion of the API. This is a breaking change between v1.0 and v1.2 — but where could we have detected this? The only sensible place to detect and report this change is v1.1, since the v1.1 -> v1.2 change is just a deletion of a |
The above proposal sounds correct to me; I can't think of any uses in PyO3 which would not fit in the above. |
@tonowak pointed out another edge case to consider: auto-traits in Normally it's fine for macro internals to be hidden and not public API even though their types are technically But consider a use case like the Are we stuck picking between "always report auto-trait changes on hidden items, and suffer false-positives" and "never report auto-trait changes on hidden items, and suffer false-negatives"? Is there a better solution? |
Another interesting edge case: if a non-sealed trait has a If the Brought up here: https://hachyderm.io/@jsbarretto@social.coop/111323903224194053 [0]: My post "A definitive guide to sealed traits in Rust" shows how to make a trait method not allowed to be called or overridden outside of the defining crate. If that method has a default impl, then the trait itself is not sealed because of it but that method's default impl may not be overridden. In such a case, |
I feel like |
FWIW I agree. The linked thread has more info, but TL;DR there was a concern about the quality of error messages being degraded for some reason if the trait is sealed. It might be worth filing a diagnostics issue on rustc for that as well. |
The main use case for PyO3 is types which are supposed to be implemented, i.e. cannot be sealed, but never manually, i.e. only via proc-macro generated code. |
Resolved by #576. Headline feature of cargo-semver-checks v0.25, planned for release shortly after Rust 1.74. Official announcement blog post coming soon on my blog: https://predr.ag/blog Thanks to everyone for your patience! Thanks to @davidhewitt in particular for bouncing ideas and prototypes back and forth until we got to something viable. Also thanks to everyone that sponsors my work — the best way to help |
Steps to reproduce the bug with the above code
It is possible to use
#[doc(hidden)]
to implement seemingly-breaking changes without actually breaking semver compatibility.Since
cargo-semver-checks
only uses the json rustdoc output to form its analysis, the claim thatcargo-semver-checks
has no false positives should probably have this shortcoming noted in the readme.e.g. these two versions of the same code below are technically semver compatible:
as merely hiding a type (or as is more often, a method) from the documentation doesn't break the semver contract.
or, for a more interesting case, the following:
There is a lot more dark magic that can done that would break docs but preserve backwards compatibility. Relying solely on the docs is a great and efficient way of implementing straightforward semver breakage cases, but I don't think it's fair to claim that any tool using this approach has no false positives.
Actual Behaviour
A breaking change is reported.
Expected Behaviour
No breaking change should be reported (though I realize this is fundamentally not possible for this crate as it is written).
As such, the "no false positives" claim should be removed or at least the caveats thoroughly mentioned.
Additional Context
No response
Debug Output
No response
The text was updated successfully, but these errors were encountered: