Skip to content

add: unnecessary_std_linkage#17305

Open
bushrat011899 wants to merge 1 commit into
rust-lang:masterfrom
bushrat011899:unnecessary_std_linkage
Open

add: unnecessary_std_linkage#17305
bushrat011899 wants to merge 1 commit into
rust-lang:masterfrom
bushrat011899:unnecessary_std_linkage

Conversation

@bushrat011899

@bushrat011899 bushrat011899 commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Objective

Solution

Added a new lint, unnecessary_std_linkage, which checks if the current crate directly uses items from std, and if it does not, suggests making the crate #![no_std].

Issues

  • Final artifacts (e.g., binary crates) can depend on the panic_handler or global_allocator provided by std. Marking a crate #![no_std] will cause it to fail to compile if a user does not provide appropriate globals. I think a lint like this would be most useful if it was enabled by default, since it could be an entry point to start adding no_std support to a crate. Either:
    1. Allow this lint by default. Unfortunate since adding this lint is about as much effort as just adding #![no_std] extern crate std; to a crate.
    2. Only apply this lint to crate_type = "lib" crates. Probably the best approach? This is what I've chosen for now.
    3. Add configuration to the lint to specify the crate types permissible. Seems a bit much to me personally.
  • The implicit prelude cannot be trivially checked for usage, since single identifiers are not paths passed into LateLintPass::check_path. Since the only std-only items in the implicit prelude are macros (which include std paths in their expansions), println/etc. are all appropriately detected by this lint. What isn't checked is usage of alloc items such as Box, Vec, ToOwned, etc. This is workable, since the user can also add extern crate alloc; and explicitly import all of those items, but it's not something I can see a way to resolve within this lint directly. Maybe it would require a using_alloc_from_implicit_prelude lint on its own, but I have no idea how to detect that.
  • This lint only checks within a crate, it does not check that dependencies are also no_std compatible. Tracking Issue for build-std explicit dependencies cargo#16960 is probably the appropriate avenue for that particular guarantee.

Notes

  • No AI tooling of any kind was used during the creation of this PR.
  • Creating as a draft as I think this needs some revision work, especially how alloc is introduced. I've decided the best course of action is to just inform the user they may want to also add extern crate alloc; but not include it as a suggestion. Better to have a simple suggestion that may require work than a complex suggestion that still may require work.

Please write a short comment explaining your change (or "none" for internal only changes)

changelog: add [unnecessary_std_linkage] to indicate when a crate could be a candidate for no_std compatibility.

@rustbot rustbot added the needs-fcp PRs that add, remove, or rename lints and need an FCP label Jun 25, 2026
@bushrat011899 bushrat011899 force-pushed the unnecessary_std_linkage branch from 848e33d to 0625f97 Compare June 25, 2026 13:17
@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown

Lintcheck changes for b4aa1f2

Lint Added Removed Changed
clippy::unnecessary_std_linkage 3 0 0

This comment will be updated if you push new changes

@bushrat011899 bushrat011899 force-pushed the unnecessary_std_linkage branch 6 times, most recently from 2ffcf6b to f93d068 Compare June 25, 2026 22:47
@bushrat011899

Copy link
Copy Markdown
Contributor Author

The 3 unnecessary_std_linkage emissions are all valid detections.

  • clap 4.5.9 only re-exports other crates; it does not use std itself.
  • quote 1.0.36 only needs items from alloc, but doesn't mark itself as no_std. It is intended for use in procedural macros, but it is also valid to use as a library itself.
  • unicode-normalization 0.1.23 does enable no_std when the std feature isn't enabled, but it doesn't use std even when the feature is enabled.

Honestly surprised out of the 200 crates lintcheck uses that any were detected by this lint; I would've expected such foundational crates to already have this thoroughly litigated.

@bushrat011899 bushrat011899 marked this pull request as ready for review June 25, 2026 23:07
@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Jun 25, 2026
@rustbot

rustbot commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator

r? @Jarcho

rustbot has assigned @Jarcho.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: 8 candidates
  • 8 candidates expanded to 8 candidates
  • Random selection from Jarcho, dswij, llogiq, samueltardieu

@bushrat011899

Copy link
Copy Markdown
Contributor Author

@rustbot reroll

I've explicitly assigned an extra PR to Jarcho already for the std_instead_of_core lints, which this PR is unrelated to. I feel like it's only fair to reroll to balance it out.

@rustbot rustbot assigned llogiq and unassigned Jarcho Jun 25, 2026
@bushrat011899

Copy link
Copy Markdown
Contributor Author

To give a better estimate of the impact of this lint (as-is), I ran lintcheck locally on the top 2,000 crates from crates.io, and found 124 instances of crates which (under the default configuration) link std without actually using it.

custom.toml with only the 124 crates this lint is triggered for
[crates]
alloy-eip2124 = { name = 'alloy-eip2124', version = '0.2.0' }
alloy-eip2930 = { name = 'alloy-eip2930', version = '0.2.3' }
alloy-eip7702 = { name = 'alloy-eip7702', version = '0.6.3' }
alloy-eips = { name = 'alloy-eips', version = '2.1.0' }
alloy-json-abi = { name = 'alloy-json-abi', version = '1.6.0' }
alloy-serde = { name = 'alloy-serde', version = '2.1.0' }
alloy-sol-type-parser = { name = 'alloy-sol-type-parser', version = '1.6.0' }
alloy-sol-types = { name = 'alloy-sol-types', version = '1.6.0' }
annotate-snippets = { name = 'annotate-snippets', version = '0.12.16' }
async-object-pool = { name = 'async-object-pool', version = '0.2.0' }
atoi = { name = 'atoi', version = '3.1.0' }
bitmaps = { name = 'bitmaps', version = '3.2.1' }
bon = { name = 'bon', version = '3.9.3' }
byte-unit = { name = 'byte-unit', version = '5.2.3' }
cfg_aliases = { name = 'cfg_aliases', version = '0.2.1' }
ciborium = { name = 'ciborium', version = '0.2.2' }
clap = { name = 'clap', version = '4.6.1' }
clipboard-win = { name = 'clipboard-win', version = '5.4.1' }
constant_time_eq = { name = 'constant_time_eq', version = '0.5.0' }
core2 = { name = 'core2', version = '0.0.0' }
cranelift-native = { name = 'cranelift-native', version = '0.133.1' }
crc32fast = { name = 'crc32fast', version = '1.5.0' }
crossbeam = { name = 'crossbeam', version = '0.8.4' }
curl-sys = { name = 'curl-sys', version = '0.4.89+curl-8.20.0' }
cxxbridge-flags = { name = 'cxxbridge-flags', version = '1.0.194' }
diff = { name = 'diff', version = '0.1.13' }
event-listener-strategy = { name = 'event-listener-strategy', version = '0.5.4' }
fastbloom = { name = 'fastbloom', version = '0.17.0' }
fiat-crypto = { name = 'fiat-crypto', version = '0.3.0' }
fixed-hash = { name = 'fixed-hash', version = '0.8.0' }
fsevent-sys = { name = 'fsevent-sys', version = '5.2.0' }
gemm = { name = 'gemm', version = '0.19.0' }
gemm-c32 = { name = 'gemm-c32', version = '0.19.0' }
gemm-c64 = { name = 'gemm-c64', version = '0.19.0' }
gemm-f16 = { name = 'gemm-f16', version = '0.19.0' }
gemm-f32 = { name = 'gemm-f32', version = '0.19.0' }
gemm-f64 = { name = 'gemm-f64', version = '0.19.0' }
gix-trace = { name = 'gix-trace', version = '0.1.20' }
glam = { name = 'glam', version = '0.33.1' }
gloo-timers = { name = 'gloo-timers', version = '0.4.0' }
glutin_wgl_sys = { name = 'glutin_wgl_sys', version = '0.6.1' }
icu_locid = { name = 'icu_locid', version = '2.0.0' }
icu_locid_transform = { name = 'icu_locid_transform', version = '2.0.0' }
impl-codec = { name = 'impl-codec', version = '0.7.1' }
inotify-sys = { name = 'inotify-sys', version = '0.1.5' }
ipconfig = { name = 'ipconfig', version = '0.3.4' }
khronos_api = { name = 'khronos_api', version = '3.1.0' }
lab = { name = 'lab', version = '0.11.0' }
lexical-core = { name = 'lexical-core', version = '1.0.6' }
lexical-parse-float = { name = 'lexical-parse-float', version = '1.0.6' }
lexical-parse-integer = { name = 'lexical-parse-integer', version = '1.0.6' }
lexical-write-float = { name = 'lexical-write-float', version = '1.0.6' }
lexical-write-integer = { name = 'lexical-write-integer', version = '1.0.6' }
libsecp256k1 = { name = 'libsecp256k1', version = '0.7.2' }
libz-rs-sys = { name = 'libz-rs-sys', version = '0.6.4' }
logos = { name = 'logos', version = '0.16.1' }
mac = { name = 'mac', version = '0.1.1' }
maplit = { name = 'maplit', version = '1.0.2' }
maybe-rayon = { name = 'maybe-rayon', version = '0.1.1' }
merlin = { name = 'merlin', version = '3.0.0' }
miow = { name = 'miow', version = '0.6.1' }
normalize-line-endings = { name = 'normalize-line-endings', version = '0.3.0' }
ntapi = { name = 'ntapi', version = '0.4.3' }
num_enum = { name = 'num_enum', version = '0.7.6' }
nybbles = { name = 'nybbles', version = '0.4.8' }
opaque-debug = { name = 'opaque-debug', version = '0.4.0' }
opentelemetry-semantic-conventions = { name = 'opentelemetry-semantic-conventions', version = '0.32.0' }
option-ext = { name = 'option-ext', version = '0.2.0' }
overload = { name = 'overload', version = '0.1.1' }
owned_ttf_parser = { name = 'owned_ttf_parser', version = '0.25.1' }
pnet = { name = 'pnet', version = '0.35.0' }
powerfmt = { name = 'powerfmt', version = '0.2.0' }
precomputed-hash = { name = 'precomputed-hash', version = '0.1.1' }
pretty_assertions = { name = 'pretty_assertions', version = '1.4.1' }
primitive-types = { name = 'primitive-types', version = '0.14.0' }
pulp = { name = 'pulp', version = '0.22.3' }
quick-error = { name = 'quick-error', version = '2.0.1' }
quote-use = { name = 'quote-use', version = '0.8.4' }
rustc_version_runtime = { name = 'rustc_version_runtime', version = '0.3.0' }
rustls-platform-verifier-android = { name = 'rustls-platform-verifier-android', version = '0.1.1' }
schannel = { name = 'schannel', version = '0.1.29' }
secp256k1-sys = { name = 'secp256k1-sys', version = '0.13.0' }
security-framework = { name = 'security-framework', version = '3.7.0' }
serde_spanned = { name = 'serde_spanned', version = '1.1.1' }
serde_yml = { name = 'serde_yml', version = '0.0.13' }
signal-hook-mio = { name = 'signal-hook-mio', version = '0.3.0' }
simd_cesu8 = { name = 'simd_cesu8', version = '1.1.1' }
slug = { name = 'slug', version = '0.1.6' }
target-triple = { name = 'target-triple', version = '1.0.0' }
tauri-plugin = { name = 'tauri-plugin', version = '2.6.3' }
test-case = { name = 'test-case', version = '3.3.1' }
test-log = { name = 'test-log', version = '0.2.21' }
testcontainers-modules = { name = 'testcontainers-modules', version = '0.15.0' }
thread-id = { name = 'thread-id', version = '5.1.0' }
toml_datetime = { name = 'toml_datetime', version = '1.1.1+spec-1.1.0' }
toml_parser = { name = 'toml_parser', version = '1.1.2+spec-1.1.0' }
tree-sitter-python = { name = 'tree-sitter-python', version = '0.25.0' }
tree-sitter-rust = { name = 'tree-sitter-rust', version = '0.24.2' }
tree-sitter-typescript = { name = 'tree-sitter-typescript', version = '0.23.2' }
uds_windows = { name = 'uds_windows', version = '1.2.1' }
unic-langid = { name = 'unic-langid', version = '0.9.6' }
unicode-normalization = { name = 'unicode-normalization', version = '0.1.25' }
unicode-vo = { name = 'unicode-vo', version = '0.1.0' }
unicode_categories = { name = 'unicode_categories', version = '0.1.1' }
unicode_names2 = { name = 'unicode_names2', version = '3.1.0' }
vergen = { name = 'vergen', version = '10.0.1' }
vswhom-sys = { name = 'vswhom-sys', version = '0.1.3' }
vte = { name = 'vte', version = '0.15.0' }
vtparse = { name = 'vtparse', version = '0.7.0' }
wasm-bindgen-futures = { name = 'wasm-bindgen-futures', version = '0.4.76' }
wayland-protocols = { name = 'wayland-protocols', version = '0.32.13' }
wayland-protocols-plasma = { name = 'wayland-protocols-plasma', version = '0.3.12' }
wayland-protocols-wlr = { name = 'wayland-protocols-wlr', version = '0.3.12' }
web_atoms = { name = 'web_atoms', version = '0.2.5' }
winapi = { name = 'winapi', version = '0.3.9' }
winapi-util = { name = 'winapi-util', version = '0.1.11' }
windows-core = { name = 'windows-core', version = '0.62.2' }
windows-numerics = { name = 'windows-numerics', version = '0.3.1' }
windows-registry = { name = 'windows-registry', version = '0.6.1' }
windows-strings = { name = 'windows-strings', version = '0.5.1' }
windows-threading = { name = 'windows-threading', version = '0.2.1' }
windows-version = { name = 'windows-version', version = '0.1.7' }
winsafe = { name = 'winsafe', version = '0.0.27' }
xml-rs = { name = 'xml-rs', version = '1.0.0' }

So for the top 200 it's approximately 1% of crates, and in the top 2,000 that expands to 6%, which matches my expectations (more popular crates are more likely to be scrutinised for no_std compatibility).

@rustbot

This comment has been minimized.

@rustbot

This comment has been minimized.

@rustbot

This comment has been minimized.

@rustbot rustbot added has-merge-commits PR has merge commits, merge with caution. S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) labels Jul 6, 2026
@bushrat011899 bushrat011899 force-pushed the unnecessary_std_linkage branch from 15748e9 to fc1de27 Compare July 6, 2026 09:45
@rustbot rustbot removed S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) has-merge-commits PR has merge commits, merge with caution. labels Jul 6, 2026
Comment thread clippy_lints/src/unnecessary_std_linkage.rs Outdated
Comment thread clippy_lints/src/unnecessary_std_linkage.rs Outdated
@bushrat011899 bushrat011899 force-pushed the unnecessary_std_linkage branch 3 times, most recently from a48c153 to f08f3f8 Compare July 11, 2026 04:01
@rustbot

This comment has been minimized.

@bushrat011899 bushrat011899 force-pushed the unnecessary_std_linkage branch from f08f3f8 to b4aa1f2 Compare July 11, 2026 09:31
@rustbot

rustbot commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator

This PR was rebased onto a different master commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-fcp PRs that add, remove, or rename lints and need an FCP S-waiting-on-review Status: Awaiting review from the assignee but also interested parties

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Lint crates that can be #![no_std] but aren't

5 participants