Skip to content

fix: add symbol-level IMPORTS edges for named imports#137

Closed
Demonseed-jpg wants to merge 1 commit into
abhigyanpatwari:mainfrom
Demonseed-jpg:fix-symbol-imports
Closed

fix: add symbol-level IMPORTS edges for named imports#137
Demonseed-jpg wants to merge 1 commit into
abhigyanpatwari:mainfrom
Demonseed-jpg:fix-symbol-imports

Conversation

@Demonseed-jpg

Copy link
Copy Markdown

NOTE: This was fixed by AI and I am by no means a programmer. Please let me know if you have any issues with this.


Problem

The ingestion pipeline only creates file-to-file IMPORTS edges. When a file uses a named import like:

from myapp.auth import validate_user

The graph only records:

auth_controller.py (File) --IMPORTS--> auth.py (File)

The specific symbol (validate_user function) being imported is lost. This means:

  • impact("validate_user") can't trace what depends on this function via imports
  • context("validate_user") doesn't show importing files in its incoming references
  • Blast radius analysis falls back to file-level granularity, overstating impact for files that export many symbols

Solution

Add symbol-level IMPORTS edges alongside existing file-level ones by leveraging the SymbolTable (already populated during parsing) to resolve named imports to their target nodes.

After this fix, the graph contains both:

auth_controller.py (File) --IMPORTS--> auth.py (File)              ← unchanged
auth_controller.py (File) --IMPORTS--> validate_user (Function)     ← NEW

What's affected

Import pattern Before After
from X import foo, bar (Python) File → File only File → File + File → Function/Class
import { foo, bar } from 'X' (JS/TS) File → File only File → File + File → Function/Class
from X import foo as alias (Python) File → File only File → File + File → Function (original name)
import X (Python bare import) File → File No change (correct — no specific symbol imported)
import X from 'Y' (JS default import) File → File No change (default imports don't target named symbols)
from X import * (Python wildcard) File → File No change (wildcard doesn't target specific symbols)

Changes

src/core/ingestion/workers/parse-worker.ts

  • Add extractImportedSymbolNames() — walks the import AST node to extract named symbols from Python import_from_statement and JS/TS named_imports nodes
  • Attach extracted symbolNames array to the ExtractedImport interface for the worker data path

src/core/ingestion/import-processor.ts

  • Add addSymbolImportEdges() helper in both processImports (sequential path) and processImportsFromExtracted (worker path)
  • For each extracted symbol name, calls symbolTable.lookupExact(resolvedPath, name) to find the target Function/Class node
  • Creates a high-confidence IMPORTS edge from the source File to the target symbol node
  • Accept optional symbolTable parameter (no breaking change — undefined falls through gracefully)

src/core/ingestion/pipeline.ts

  • Pass the existing symbolTable as a new argument to processImports() and processImportsFromExtracted()

Design decisions

  • Additive, not replacing — file-level edges are preserved. Symbol-level edges are additional. No existing behavior changes.
  • Graceful degradation — if symbolTable is undefined or a symbol isn't found, the code silently skips. No errors for unresolved symbols.
  • Source is File, target is Symbol — the sourceId is the importing File node (not a symbol within it), since Python/JS import statements are module-level, not scoped to a function.
  • Uses lookupExact only — matches symbol by (filePath, name) pair. No fuzzy matching, keeping confidence at 1.0.

Testing

Verified on a Python LangGraph project (84 nodes). Before: 179 edges (all file-level). After: 179 file-level edges + ~40 new symbol-level IMPORTS edges correctly linking files to the specific Function/Class nodes they import.

Example results from impact("supervisor") targeting a Function node:

  • Before: no upstream edges (function node was unreachable via IMPORTS)
  • After: graph.py (File) --IMPORTS--> supervisor (Function) at depth 1, with correct transitive trace through files that import graph.py at depth 2

@vercel

vercel Bot commented Mar 1, 2026

Copy link
Copy Markdown

Someone is attempting to deploy a commit to the NexusCore Team on Vercel.

A member of the Team first needs to authorize it.

TheGrouchy added a commit to TheGrouchy/GitNexus that referenced this pull request Mar 6, 2026
…odes

- Add definition.instance query to PYTHON_QUERIES, anchored to module
  scope with call on right-hand side
- Add definition.instance to DEFINITION_CAPTURE_KEYS and label maps in
  both sequential (parsing-processor.ts) and worker (parse-worker.ts) paths
- Compose with abhigyanpatwari#137: instances are now linkable targets for File->Symbol
  IMPORTS edges created by symbol-level import resolution
@TheGrouchy

Copy link
Copy Markdown

Verified on a Python project (~180 files). Before: all IMPORTS edges were file-level only — \ returned 0 upstream callers for any named import target. After: +53 symbol-level edges created correctly.

Aliased imports (\ → resolves to ) and wildcard/bare module skipping both behave correctly. Worker path serialization handles the optional \ field cleanly via structured clone.

One thing worth confirming before merge: downstream consumers of \ across worker boundaries handle \ fields — they do, structured clone omits them and all callers check \ before use. No issues found.

Solid fix, hope it gets merged.

@magyargergo

Copy link
Copy Markdown
Collaborator

This will be covered as part of #238 as it requires semantic resolution. Thank you for your contribution!

@Demonseed-jpg

Copy link
Copy Markdown
Author

This will be covered as part of #238 as it requires semantic resolution. Thank you for your contribution!

Glad it’s being looked at! I did find an issue with it not working for all languages, just an FYI.

@magyargergo

magyargergo commented Mar 12, 2026

Copy link
Copy Markdown
Collaborator

These are the feature we are planning to support and cover in my PR

Per-Language Feature Coverage Matrix

Feature TS Java C# C++ Python Go Rust Kotlin PHP
Heritage (EXTENDS/IMPLEMENTS)
Ambiguous symbol resolution
Arity filtering
Variadic parameter handling N/A
Member-call resolution
Receiver-constrained resolution
Constructor resolution ✅¹ ✅¹
Named import disambiguation ⚠️² N/A N/A ✅³ ✅⁴
Alias import extraction N/A N/A N/A
Local shadow (same-file wins)
Re-export chain N/A N/A N/A N/A ✅⁵ N/A N/A
MRO / OVERRIDES edges N/A N/A
HAS_METHOD edges

¹ Go/Rust use struct literal T{} → resolves to Struct node (not Constructor node)
² C# using Namespace; is namespace-scoped — can't produce per-symbol NamedImportMap entries; aliased using A = B.C works
³ Now fully working for both aliased and non-aliased Rust imports. Spurious path-segment binding for grouped imports is low impact
⁴ Kotlin non-aliased class imports now covered; member imports (lowercase last segment) correctly excluded
pub use re-export chains now followed via walkBindingChain


I'm still actively working on it.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants