rust-lang / rust-lang/cargo

Lint against non semver-open dependencies

Open
#5,340 31 comments 8 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-new-lint S-needs-design
Dominant language
Rust
Stars
15.5k
Forks
3k
Avg merge
23h 30m
Merged PRs (30d)
51

Description

View all comments

Cargo more-or-less enforces contract on crate authors that version 1.x+n.u can always be used instead of 1.x.v. That means that any semver requirement, except for ^x.y.z, does not make sense. Moreover, exotic version requirements may version resolution significantly more complicated.

So we should lint against any sevmer requirement other than x, x.y or x.y.z.

Specifically,

  • ^x.y.z is not necessary, because ^ is default
  • ~ violates versioning contract. As a consequence Cargo will generally refuse to compile a project with two tilde dependencies with same major version.
  • >, >= do not make sense, because major version upgrade, by contract, can break anything.
  • = is in the wrong place, lockfiles should be used to pin dependencies.
  • <, <= again, version pining belongs to the lockfile.
  • * we already forbid. It actually makes sense for suerp-quick local development though!

The interesting not clear-cut use-case is version striding (compatibility with several major versions). For example, serde = ">= 2, < 4" means that you are source-compatible with both serde 2 and serde 3. However, looks like the proper solution here is for the upstream crate to do dtolnay's trick? That is, serde 2 should depend on serde 3, and reexport it's APIs.

There are several design decisions on how to actually implement it:

  • We can warn on cargo publish enforcing this for crate.io crates, or we can warn on parsing Cargo.toml, enforcing this for anyone.

  • We can add an opt-out mechanism. For example, we might make you to write a reason if you use non-standard requirement: foo = { version = "~9.2", reason = "this crate has weird rustc compatibility policy" }.

  • Finally, the most radical option, what if we flatly deprecate all forms of version specification, except for x, x.y, x.y.z? That way, we won't need to document and explain various forms of version requirements at all.

I personally am leaning towards the last option, because it makes the overall system radically simpler for the user, but I am curious if there are some real use-cases for exotic version requirements?

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

The issue names no files, tests, or entry points. First review Cargo's existing handling of version requirements and the proposed linting, enforcement, and opt-out alternatives; done requires an agreed policy and an implementation plan.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.