You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Continue resolving cargo-semver-checks blockers for merging into cargo #104
Design and implement cargo-semver-checks functionality that lies on the critical path for merging the tool into cargo itself. Continues the work of the 2025h2 goal.
This issue is intended for status updates only.
For general questions or comments, please contact a goal owner or post in the Zulip topic.
Key developments: We've laid a lot of the groundwork to make manifest linting possible.
In preparation for the significant expansion of our CLI, we now have the ability to test how CLI invocations get interpreted internally. We can also now snapshot the output of any CLI invocation over a given workspace, allowing us to proceed with refactoring the implementation without breaking existing use cases.
We have a concrete design for the expansion of the CLI, and for the Trustfall schema changes that will be necessary to enable manifest linting.
Not blocked on anything. No opportunities to contribute yet, but stay tuned — there will be some in future updates!
This is SemVer-major because it'll break uses like: my_dep = { version = "1.0", features = ["removed_feature"] }. We're ignoring any features named nightly because community consensus is that such features are unstable — I'll do some more research on other expected-unstable feature patterns in the community before we ship any lints around this.
Not blocked on anything. No opportunities to contribute yet, but stay tuned — there will be some in future updates!
This newly allows the implementation of breaking-change lints such as "generic const parameter no longer has default value" or "trait bound removed from generic parameter in trait method."
Additionally, @suaviloquence has implemented the ability to print rustdoc JSON type information into human-readable source code form, which will allow us to produce higher-quality diagnostics for type-related breaking changes. This required a lot of care, perseverance, and testing — thank you 🙏
Resolved multi-team questions that were blocking cross-crate checking
For a long time, cross-crate checking was blocked on a multi-team coordination problem: cargo-semver-checks needs more data from rustdoc, which in turn needs more data from rustc. This was a classic "everyone's third-most important thing" problem: important, but not critical and therefore never top of the agenda for resolving the open questions there.
In-person meetings with Rust Project members during my trips to RustConf and EuroRust helped nudge this forward:
The compiler team MCP intended to provide additional data to rustdoc was merged and can now be implemented.
I synced up with T-rustdoc members on how rustdoc would then present that data to cargo-semver-checks in rustdoc JSON. Along the way, we were able to discuss and agree on several additional improvements to rustdoc JSON that improved performance in cargo-semver-checks.
Refactor of cargo-semver-checks data handling
Plumbing in manifest information was proving challenging due to a significant amount of tech debt accrued in the cargo-semver-checks code that loads the data of scanned crates. This PR is a significant clean-up and refactor of that portion of the tool, allowing us to move forward with retrieving manifest information while also supporting other quality-of-life improvements.
With the mountain of tech debt largely resolved, I expect that adding manifest information should be relatively straightforward from here.
This portion of the goal also greatly benefited from the Google Summer of Code work that @suaviloquence did — it wouldn't have been possible without the significant effort he put into improving our test suites and harnesses.
As a recap, the idea here is catching breakage caused by manifest (Cargo.toml) changes, not just source code changes.
An example of such breakage is the removal of a package feature: any crates that enabled the removed feature will no longer build.
The PR above builds upon the data-handling refactor from the previous update post, and makes it possible to lint manifests for breaking changes. The PR includes the feature_missing lint that checks for the exact case mentioned above. That is but one of many new lints that are now possible with this feature. Hooray! 🎉
A concrete plan for how that new info turns into dozens of new lints covering many kinds of bounds
Linting ?Sized and 'static bounds turned out to be quite a bit more complex than I anticipated. The key issue is that seeing T: Foo + ?Sized does not guarantee that T can be unsized, since we might have Foo: Sized which renders the ?Sized relaxation ineffective. Similarly, seeing T: Foo might also non-obviously imply T: 'static via a similar implied bound.
Failure to correctly account for implied bounds would lead to catastrophic false-positives and false-negatives. For example, changing T: Foo to T: Foo + 'static could be a major breaking change or a no-op, depending on whether we have Foo: 'static (either directly or implicitly via other trait bounds).
We cannot determine implied bounds using information present in rustdoc JSON today, so the rustdoc team and I have been iterating on the best way to compute and include that information in rustdoc JSON. Assuming something similar to the aforementioned PR becomes part of rustdoc JSON, cargo-semver-checks stands to gain several dozen new lints covering these tricky cases over trait associated types, generic type parameters, and APIT/RPIT/RPITIT.
Once that's resolved, we'll have enough here for a basic prototype. Getting features right in dependencies will likely require more work due to having many more cargo-related edge cases.
There's been significant internal progress. As I described in my year-end overview post, we're aiming to solve cargo-semver-checks' key limitations, which requires solving several hard problems at once.
We're about 9000 lines of code deep into implementing a normalized type representation, a prerequisite for efficient type-checking lints. The gist of the idea is:
We want to detect when types may have changed, so we can trigger a type-checking run.
That type-checking run is fairly expensive: we need to generate a witness program, and run cargo check on it up to twice.
Rust has many ways to make syntactic changes to types that are semantically irrelevant. For example, consider renaming all 'a lifetimes to 'b — this shouldn't run type-checking, because nothing semantic changed so there cannot be a Semantic Versioning violation.
We expect we're a handful of PRs away from having a suitable implementation of this normalization process. Our focus will then pivot toward cross-crate rustdoc JSON use, which will both address a number of false-positives and false-negatives per se, and is another prerequisite for type-checking lints.
Engaging with these hard problems is also producing benefits for Rustaceans today. First, digging deeply into the codebase helps discover bugs and edge cases, and then it produces a perfect opportunity to address them. For example, here are a couple of fixes that will be part of the next cargo-semver-checks release:
At the request of Rust project members, we took a brief detour to implement breakage-checking for the Rust standard library. This work is very close tothe finish line and involved about 15kLoC across 30+ PRs, largely in order to handle stability attributes correctly. I plan to publish a blog post on it when it's complete.
Like in the previous post, this process also caused us to discover and fix pre-existing issues in cargo-semver-checks affecting users today. For example, two lints had existing false-positives fixed as a result of us reasoning through the (much more complex) edge cases of partial stability in the Rust standard library.
Additionally, we also improved the type normalization code to be more robust to generic parameter additions or removals. Previously, all generic parameters of the same kind (types, lifetimes, consts) shared the same normalization namespace. This caused issues. By way of analogy:
impl<A>Foo<A>{fnbar<B>(_:B){}}
If Foo gains another generic type parameter, that new parameter would get normalized to B instead. This would change the normalization of bar<B> into bar<C>, making it normalize differently ("appear to change"). This would have triggered many unnecessary witness checks and would have been tediously slow. With the latest fixes, generics on a parent scope no longer change the normalization of inner scopes' generics, avoiding this problem.
Given recent improvements in AI models' cybersecurity capabilities, I've pivoted toward hardening the cargo-semver-checks GitHub Actions security posture. This work is largely done for the main repo and its primary dependencies that I also maintain.
As a smaller work item, I've also taken the opportunity to add SemVer lints for breakage related to variadic functions. That functionality will be stable in the upcoming Rust 1.99, which makes this the upcoming cargo-semver-checks release the first time we'll have effectively "day 1" SemVer linting support for a new language feature!
Activity
nikomatsakis commentedon Jul 25, 2024
This issue is intended for status updates only.
For general questions or comments, please contact the owner(s) directly.
obi1kenobi commentedon Sep 1, 2024
Status update as of September 1.
Key developments: We've laid a lot of the groundwork to make manifest linting possible.
Not blocked on anything. No opportunities to contribute yet, but stay tuned — there will be some in future updates!
obi1kenobi commentedon Sep 17, 2024
Status update as of September 17.
Key developments: Working prototype of manifest querying.
WIP branch here: https://github.com/obi1kenobi/trustfall-rustdoc-adapter/compare/manifest_querying
An example SemVer lint this enables would be: "a non-
nightly, non-underscore-prefixed feature has been removed from the package between releases."{ CrateDiff { baseline { feature { feature: name @filter(op: "!=", value: ["$nightly"]) @tag @output } } current { feature @fold @transform(op: "count") @filter(op: "=", value: ["$zero"]) { name @filter(op: "=", value: ["%feature"]) } } } } # args: { "nightly": "nightly", "zero": 0 }This is SemVer-major because it'll break uses like:
my_dep = { version = "1.0", features = ["removed_feature"] }. We're ignoring any features namednightlybecause community consensus is that such features are unstable — I'll do some more research on other expected-unstable feature patterns in the community before we ship any lints around this.Not blocked on anything. No opportunities to contribute yet, but stay tuned — there will be some in future updates!
obi1kenobi commentedon Sep 30, 2024
No major updates to report. Preparing my talk for next week's EuroRust has taken away most of my free time.
obi1kenobi commentedon Nov 20, 2024
Key developments:
Partial schema design and implementation of type information in lints
The schema we use for writing lints now contains information on:
This newly allows the implementation of breaking-change lints such as "generic const parameter no longer has default value" or "trait bound removed from generic parameter in trait method."
Additionally, @suaviloquence has implemented the ability to print rustdoc JSON type information into human-readable source code form, which will allow us to produce higher-quality diagnostics for type-related breaking changes. This required a lot of care, perseverance, and testing — thank you 🙏
Resolved multi-team questions that were blocking cross-crate checking
For a long time, cross-crate checking was blocked on a multi-team coordination problem: cargo-semver-checks needs more data from rustdoc, which in turn needs more data from rustc. This was a classic "everyone's third-most important thing" problem: important, but not critical and therefore never top of the agenda for resolving the open questions there.
In-person meetings with Rust Project members during my trips to RustConf and EuroRust helped nudge this forward:
Refactor of cargo-semver-checks data handling
Plumbing in manifest information was proving challenging due to a significant amount of tech debt accrued in the cargo-semver-checks code that loads the data of scanned crates. This PR is a significant clean-up and refactor of that portion of the tool, allowing us to move forward with retrieving manifest information while also supporting other quality-of-life improvements.
With the mountain of tech debt largely resolved, I expect that adding manifest information should be relatively straightforward from here.
This portion of the goal also greatly benefited from the Google Summer of Code work that @suaviloquence did — it wouldn't have been possible without the significant effort he put into improving our test suites and harnesses.
obi1kenobi commentedon Dec 1, 2024
Key developments:
feature_missinglint obi1kenobi/cargo-semver-checks#1007Cargo manifest linting
As a recap, the idea here is catching breakage caused by manifest (
Cargo.toml) changes, not just source code changes.An example of such breakage is the removal of a package feature: any crates that enabled the removed feature will no longer build.
The PR above builds upon the data-handling refactor from the previous update post, and makes it possible to lint manifests for breaking changes. The PR includes the
feature_missinglint that checks for the exact case mentioned above. That is but one of many new lints that are now possible with this feature. Hooray! 🎉nikomatsakis commentedon Feb 18, 2025
This is a continuing project goal, and the updates below this comment will be for the new period 2025h1
obi1kenobi commentedon Feb 19, 2025
Thanks, Niko! Copying in the new tasks from the 2025h1 period:
'staticand?Sized(@obi1kenobi)#[doc(hidden)]in sealed trait analysis (@obi1kenobi)15 remaining items
obi1kenobi commentedon Sep 19, 2025
Just removed the duplicate posts, guessing from a script that had a bad day.
obi1kenobi commentedon Nov 2, 2025
Status update as of November 1
Key developments:
Linting
?Sizedand'staticbounds turned out to be quite a bit more complex than I anticipated. The key issue is that seeingT: Foo + ?Sizeddoes not guarantee thatTcan be unsized, since we might haveFoo: Sizedwhich renders the?Sizedrelaxation ineffective. Similarly, seeingT: Foomight also non-obviously implyT: 'staticvia a similar implied bound.Failure to correctly account for implied bounds would lead to catastrophic false-positives and false-negatives. For example, changing
T: FootoT: Foo + 'staticcould be a major breaking change or a no-op, depending on whether we haveFoo: 'static(either directly or implicitly via other trait bounds).We cannot determine implied bounds using information present in rustdoc JSON today, so the rustdoc team and I have been iterating on the best way to compute and include that information in rustdoc JSON. Assuming something similar to the aforementioned PR becomes part of rustdoc JSON,
cargo-semver-checksstands to gain several dozen new lints covering these tricky cases over trait associated types, generic type parameters, and APIT/RPIT/RPITIT.obi1kenobi commentedon Nov 23, 2025
Google Summer of Code 2025 is complete + finally some movement on cross-crate linting! 🚀
Key developments
cargo-semver-checks— find more details here!cargo rustdoc --output-format=jsonsometimes doesn't rebuild for different versions of same crate name. cargo#16291obi1kenobi commentedon Jan 17, 2026
I posted a "year in review" for cargo-semver-checks here: https://predr.ag/blog/cargo-semver-checks-2025-year-in-review/
It has a section on how I think we should move forward in 2026 and beyond: https://predr.ag/blog/cargo-semver-checks-2025-year-in-review/#the-path-forward-for-2026-and-beyond
tomassedovic commentedon May 5, 2026
This is a continuing project goal, and the updates below this comment will be for the new period 2026
obi1kenobi commentedon Jun 17, 2026
There's been significant internal progress. As I described in my year-end overview post, we're aiming to solve
cargo-semver-checks' key limitations, which requires solving several hard problems at once.We're about 9000 lines of code deep into implementing a normalized type representation, a prerequisite for efficient type-checking lints. The gist of the idea is:
cargo checkon it up to twice.'alifetimes to'b— this shouldn't run type-checking, because nothing semantic changed so there cannot be a Semantic Versioning violation.We expect we're a handful of PRs away from having a suitable implementation of this normalization process. Our focus will then pivot toward cross-crate rustdoc JSON use, which will both address a number of false-positives and false-negatives per se, and is another prerequisite for type-checking lints.
Engaging with these hard problems is also producing benefits for Rustaceans today. First, digging deeply into the codebase helps discover bugs and edge cases, and then it produces a perfect opportunity to address them. For example, here are a couple of fixes that will be part of the next
cargo-semver-checksrelease:#[doc(hidden)]applied to glob re-exports:#[doc(hidden)]can sometimes make glob re-exports not be public API. obi1kenobi/trustfall-rustdoc-adapter#1072obi1kenobi commentedon Aug 1, 2026
At the request of Rust project members, we took a brief detour to implement breakage-checking for the Rust standard library. This work is very close to the finish line and involved about 15kLoC across 30+ PRs, largely in order to handle stability attributes correctly. I plan to publish a blog post on it when it's complete.
Like in the previous post, this process also caused us to discover and fix pre-existing issues in
cargo-semver-checksaffecting users today. For example, two lints had existing false-positives fixed as a result of us reasoning through the (much more complex) edge cases of partial stability in the Rust standard library.Additionally, we also improved the type normalization code to be more robust to generic parameter additions or removals. Previously, all generic parameters of the same kind (types, lifetimes, consts) shared the same normalization namespace. This caused issues. By way of analogy:
If
Foogains another generic type parameter, that new parameter would get normalized toBinstead. This would change the normalization ofbar<B>intobar<C>, making it normalize differently ("appear to change"). This would have triggered many unnecessary witness checks and would have been tediously slow. With the latest fixes, generics on a parent scope no longer change the normalization of inner scopes' generics, avoiding this problem.obi1kenobi commentedon Sep 27, 2026
Given recent improvements in AI models' cybersecurity capabilities, I've pivoted toward hardening the
cargo-semver-checksGitHub Actions security posture. This work is largely done for the main repo and its primary dependencies that I also maintain.As a smaller work item, I've also taken the opportunity to add SemVer lints for breakage related to variadic functions. That functionality will be stable in the upcoming Rust 1.99, which makes this the upcoming
cargo-semver-checksrelease the first time we'll have effectively "day 1" SemVer linting support for a new language feature!