Skip to content

A Rust method declared only on a supertrait is attributed to the subtrait, fabricating an identity #374

Description

@agustingroh

Summary

A generic parameter bound to a trait that declares no methods of its own but
extends (: SuperTrait) one that does resolves a method call to the
subtrait instead of the trait that actually declares the method. Real Rust
resolves through the supertrait; crypto-finder attributes the call to a trait
that never declares it — a fabricated identity, not a missing one.

Reproduction

mod foo {
    pub trait SuperTrait { fn describe(&self) -> &'static str { "super" } }
}
trait Trait1: foo::SuperTrait {}

struct Marker;
impl foo::SuperTrait for Marker {}
impl Trait1 for Marker {}

fn test<T: Trait1>(x: &T) -> &'static str {
    x.describe()
}

Confirmed against a real rustc build (compiles, prints "super") and
matches rust-analyzer's own super_trait_method_resolution test.
crypto-finder resolves the call to app.Trait1.describe — Trait1 never
declares describe.

Why it is not a quick patch

rustFirstTraitBound (internal/callgraph/rust_type_semantics.go) has no
notion of supertrait relationships. Fixing it needs two facts that do not
exist today:

  1. A trait-declares-method-name table, independent of return type.
    fnReturns looks reusable but silently omits every void method
    (recordReturn returns early when ret == "") — exactly the
    crypto-relevant shape (fn update(&mut self, data: &[u8]) returns
    nothing). Reusing it would make the fix wrong for the methods that matter
    most, not just incomplete.
  2. A supertrait graph (trait Trait1: SuperTrait, OtherTrait {} can list
    several) to walk when the directly bound trait does not own the called
    method, with the same "first bound, no ambiguity resolution" policy used
    elsewhere.

Suggested approach

Build the trait-declares-method-name fact first (cheap, mechanical: walk
trait_item bodies collecting every fn name regardless of return type),
verify it does not regress the existing 140+ Rust tests, then add the
supertrait graph walk on top, one bound trait at a time, with a real
rustc-validated fixture for each new case.

Crypto relevance

Not sized against a corpus, but confirmed real in principle: RustCrypto's own
trait hierarchies use supertraits (e.g. AeadInPlace: AeadCore), so a
generic function bound to a supertrait-extending trait and calling a
supertrait method would misattribute the call.

References

  • Documented in openspec/specs/rust-callgraph-identity-resolution/spec.md,
    "Known gaps" → "A method declared only on a supertrait is attributed to the
    subtrait".
  • Pinned by TestRustParser_SupertraitBoundMethodIsAKnownUnfixedGap
    (internal/callgraph/rust_analyzer_rustc_parity_test.go) — asserts the
    current (fabricated) identity so a future fix updates this test
    deliberately instead of silently.
  • Found auditing PR Fix/rust semantic identity gaps #370 against the real rust-analyzer and rustc test
    suites.

Governing rule for this capability: "a wrong identity is worse than no
identity" — this call site should carry no identity rather than the wrong
one, until the fix above lands.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingrustlanguage: rust

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions