Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

Rust extractor: lossy structure and name-only call resolution (generic impls, traits, mods, use-lists, receivers)

Aperta
#4,285 1 commento 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
5/5
Tempo stimato
Più di una settimana
Idoneità per principianti
18/100
Tipo di issue
Bug
Chiarezza
Abbastanza chiara
Stato di attività
Attiva
Stack tecnologico
python, rust
Ambito
devtools

Direzione di ricerca

Start in graphify/extract.py at extract_rust, plus _make_id, _file_stem and the label_to_nid lookup in extract(), and reproduce with the sample file in the issue. The author has a patch and 10 regression tests, so the work is mostly reviewing the design: scoped IDs, trait impls, receiver-based call resolution and cross-file resolution. Done means the sample's orphan nodes, merged IDs and name-only edges are gone, and the regression tests pass.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

Version: graphifyy 0.7.0 (graphify/extract.py, extract_rust)

extract_rust loses most of the structure of idiomatic Rust. Reproduced with this file:

use std::fmt::{self, Display};
pub trait Reader { fn read(&self) -> u8; fn skip(&self) { helper(); } }
pub struct Foo<T> { x: T }
impl<T> Foo<T> { pub fn new(x: T) -> Self { Self { x } } pub fn go(&self) { Self::new(1); helper(); } }
impl Display for Foo<u8> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { Ok(()) } }
impl Reader for Foo<u8> { fn read(&self) -> u8 { 0 } }
mod inner { pub fn helper() {} pub struct Foo; }
pub fn helper() { inner::helper(); println!("x"); }
pub type Alias = u8; pub const K: u8 = 1; macro_rules! m { () => {} }

Structure problems

  1. Generic impls create orphan nodes. impl<T> Foo<T> / impl Foo<u8> produce nodes Foo<T> / Foo<u8>, separate from the Foo struct node. Methods hang off the orphans.
  2. Trait is dropped from impl Trait for Type. No implements edge; trait impls look like inherent impls, and several impls of the same method name for one type (e.g. three impl From<_> for E) share one ID scheme.
  3. trait_item returns without descending. Trait methods produce no nodes, so calls from default method bodies (skip -> helper) are lost.
  4. IDs ignore the inline mod path, and are case-folded. inner::helper and top-level helper get the same ID, as do inner::Foo and Foo; nodes merge and inner::helper() becomes a dropped self-call. Because _make_id lower-cases, const ITEM and fn item() also collide (first label wins). Across files, _file_stem is parent.stem, so every src/lib.rs shares src.lib.
  5. Missing node kinds: mod, type, const, static, union, macro_rules!.
  6. use handling is lossy: use std::fmt::{self, Display} yields one edge (the text before {), losing Display; nested/aliased lists are not flattened.

Call-resolution problems

The extractor resolves calls by bare name only (label_to_nid[name.lower()], last definition wins), ignoring qualifiers and receivers, so on a real workspace most edges for common names are wrong. Examples found by auditing the old output line by line:

  • Vec::new(), Cursor::new(..), HashMap::new(), u64::from(x), Default::default() are linked to any local fn new / fn from / fn default.
  • v.len() / s.is_empty() / file.seek(..) on std types are linked to local methods with the same name.
  • Value::Inline(..) / VrIndex(..) (enum variant / tuple-struct constructors) are linked to a function named inline / the type node.
  • validator.enter_item(..) is linked to an unrelated free fn enter_item, not the method on validator's type.
  • Conversely, true calls are missed: self.m() when several types in the file define m, Type::f() when Type has several impls, trait-qualified calls (Seek::seek(&mut s)), calls through generic bounds (V: Trait), if let Some(x) = x, loop/closure variables, and everything crossing a file boundary other than by unique name.

Patch

I have a patch (extract_rust replacement + a small Rust pass in extract() for cross-file resolution) and 10 regression tests. Happy to open a PR. Summary of the behaviour:

  • IDs scoped by file path + mod path and tagged by kind; generics stripped so impl<T> Foo<T> attaches to Foo; implements edges (also cross-file); trait methods as nodes; nodes for mod/type/const/static/macro; use-trees flattened.
  • Calls resolved with light receiver-type inference (parameters, typed/constructor let, struct field types, Self, fn/method return types with Result/Option/Box/Arc unwrapped, if let Some(x), generic bounds, for/closure element types over Vec/slice/array). When a call can reach several impls, an edge goes to each candidate with INFERRED confidence. Calls on external types (Vec::new, u64::from, ...) and enum-variant/tuple-struct constructors produce no edge. Typed calls whose type lives in another file are resolved in a cross-file pass.

Measured on a real workspace

179 .rs files (a DICOM parser; calls counted over the whole graph, incl. cross-file), old extractor vs patched:

old patched
nodes (in-file extraction) 2658 5782+
calls edges 3544 4607 (2482 EXTRACTED, 2125 INFERRED)

The 134 old call edges that disappear were classified by source line: 68 Type::f() on external types, 43 enum-variant/tuple-struct constructors and chains on external types, 23 recv.f() on std types (len/is_empty/seek/read) — plus 4 validator.enter_* calls that used to point at the wrong free function and now point at the method. None was a Type::f() call on a type defined in the workspace.

Remaining limit

Where the type is only known to a real type checker (results of .map(..) / .collect(), macro-generated code), no edge is emitted rather than a guessed one.

Lingua principale
Python
Stelle
124k
Fork
11.9k
Metriche di merge delle PR
Nessuna PR unita negli ultimi 30g

Preparare l'ambiente

Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di Graphify-Labs/graphify

Tutte le issue di Graphify-Labs/graphify

Issue simili

Altre issue su Python

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.