Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

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

Abierto
#4,285 1 comentario 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
5/5
Tiempo estimado
Más de una semana
Aptitud para principiantes
18/100
Tipo de issue
Error
Claridad
Bastante claro
Estado de actividad
Activo
Stack tecnológico
python, rust
Área
devtools

Línea de trabajo

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.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

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.

Lenguaje dominante
Python
Estrellas
124k
Forks
11.9k
Métricas de merge de PR
Sin PR fusionados en 30 d

Preparar el entorno

Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de Graphify-Labs/graphify

Todos los issues de Graphify-Labs/graphify

Issues similares

Más issues de Python

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.