rust-lang / rust-lang/rust-clippy

unnecessary_cast: type-alias suppression depends on how the casted local was initialized (mem::zeroed vs MaybeUninit::assume_init)

Open
#17,166 0 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

I-false-positive
Dominant language
Rust
Stars
13.5k
Forks
2.2k
Avg merge
2d 10h
Merged PRs (30d)
32

Description

Summary

unnecessary_cast suppresses casts of values whose type might be a cfg-dependent type alias (e.g. libc struct fields) by conservatively skipping when the cast source involves an external function it can't inspect. But that suppression only matches free functions (Res::Def(DefKind::Fn, _) on a path expr), so two semantically equivalent initializations of the same local lint differently: initializing via std::mem::zeroed() suppresses the lint, while initializing via MaybeUninit::uninit() + .assume_init() does not — assume_init is a method call (no callee path expr) and MaybeUninit::uninit resolves to DefKind::AssocFn, which the check doesn't match.

The cast in the reproducer is genuinely platform-dependent (statvfs.f_blocks is fsblkcnt_t, not u64 on every target), so the lint's own rationale for the alias suppression applies equally to both forms. The practical effect is that migrating FFI out-params from mem::zeroed() to the more idiomatic MaybeUninit surfaces this false positive and forces an #[allow].

Lint Name

unnecessary_cast

Reproducer

I tried this code (with libc = "0.2", on x86_64-unknown-linux-gnu):

fn with_zeroed(path: &std::ffi::CStr) -> u64 {
    let mut s: libc::statvfs = unsafe { std::mem::zeroed() };
    unsafe { libc::statvfs(path.as_ptr(), &mut s) };
    s.f_blocks as u64
}

fn with_maybe_uninit(path: &std::ffi::CStr) -> u64 {
    let mut s = std::mem::MaybeUninit::<libc::statvfs>::uninit();
    unsafe { libc::statvfs(path.as_ptr(), s.as_mut_ptr()) };
    let s = unsafe { s.assume_init() };
    s.f_blocks as u64
}

I expected to see this happen: both functions treated the same — no lint on either, since f_blocks is the cfg-dependent alias fsblkcnt_t and the cast is required on targets where it isn't u64.

Instead, this happened: only the MaybeUninit version lints:

warning: casting to the same type is unnecessary (`u64` -> `u64`)
  --> src/main.rs:11:5
   |
11 |     s.f_blocks as u64
   |     ^^^^^^^^^^^^^^^^^ help: try: `s.f_blocks`

The cause is in is_cast_from_ty_alias: walking the local's initializer, std::mem::zeroed is a path resolving to DefKind::Fn and hits the "External function, we can't know, better be safe" break, while MaybeUninit::uninit resolves to DefKind::AssocFn (not matched) and assume_init is an ExprKind::MethodCall (never seen as a path), so no suppression applies. Field accesses on external structs aren't examined at all, so the actual alias (fsblkcnt_t) is invisible either way.

Related: #8018, #8093 (alias/cfg-dependent FPs in this lint), #6466 (same libc-field pattern in useless_conversion).

Version
rustc 1.96.0 (ac68faa20 2026-05-25)
clippy 0.1.96 (ac68faa20c 2026-05-25)
Additional Labels

@rustbot label +I-false-positive

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

Start in clippy_lints/src/casts/unnecessary_cast.rs at is_cast_from_ty_alias, then compare how the reproducer’s mem::zeroed and MaybeUninit initialization expressions are resolved while walking the local initializer. Use the provided with_zeroed and with_maybe_uninit examples as the regression case; done means both equivalent initializations avoid the unnecessary_cast warning for the cfg-dependent libc field.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.