Rust extractor: lossy structure and name-only call resolution (generic impls, traits, mods, use-lists, receivers)
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 18/100
Research direction
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.
Written by the indexing model from the issue text.
Description
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
- Generic impls create orphan nodes.
impl<T> Foo<T>/impl Foo<u8>produce nodesFoo<T>/Foo<u8>, separate from theFoostruct node. Methods hang off the orphans. - Trait is dropped from
impl Trait for Type. Noimplementsedge; trait impls look like inherent impls, and several impls of the same method name for one type (e.g. threeimpl From<_> for E) share one ID scheme. trait_itemreturns without descending. Trait methods produce no nodes, so calls from default method bodies (skip -> helper) are lost.- IDs ignore the inline
modpath, and are case-folded.inner::helperand top-levelhelperget the same ID, as doinner::FooandFoo; nodes merge andinner::helper()becomes a dropped self-call. Because_make_idlower-cases,const ITEMandfn item()also collide (first label wins). Across files,_file_stemisparent.stem, so everysrc/lib.rssharessrc.lib. - Missing node kinds:
mod,type,const,static,union,macro_rules!. usehandling is lossy:use std::fmt::{self, Display}yields one edge (the text before{), losingDisplay; 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 localfn 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 namedinline/ the type node.validator.enter_item(..)is linked to an unrelated freefn enter_item, not the method onvalidator's type.- Conversely, true calls are missed:
self.m()when several types in the file definem,Type::f()whenTypehas 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 +
modpath and tagged by kind; generics stripped soimpl<T> Foo<T>attaches toFoo;implementsedges (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 withResult/Option/Box/Arcunwrapped,if let Some(x), generic bounds,for/closure element types overVec/slice/array). When a call can reach several impls, an edge goes to each candidate withINFERREDconfidence. 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.
- Dominant language
- Python
- Stars
- 124k
- Forks
- 11.9k
- PR merge metrics
- No merged PRs in 30d
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from Graphify-Labs/graphify
-
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
Graphify-Labs/graphify#3763 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Graphify-Labs/graphify#3611 · 1 comment ·
Maintainers usually reply within 1 day
-
Nix supportOpen
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
Graphify-Labs/graphify#3193 · 2 reactions ·
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 92/100
Graphify-Labs/graphify#2871 · 2 comments ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
Graphify-Labs/graphify#2865 ·
Maintainers usually reply within 1 day
All issues in Graphify-Labs/graphify
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
-
Difficulty 1/5 Under an hour Newbie friendliness 62/100
-
Difficulty 1/5 Under an hour Newbie friendliness 70/100
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
topoteretes/cognee#5773 ·
Maintainers usually reply within 1 day
-
MiKTeX: a failed build reports no errors because texify --quiet switches the log to file:line: styleOpen
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
SublimeText/LaTeXTools#1718 ·