Skip to content

fix(extractor): attribute file-scope calls to synthetic <file> caller (TS/JS, Python) - #722

Open
lalith0407 wants to merge 1 commit into
Egonex-AI:mainfrom
lalith0407:fix/extractor-file-scope-calls
Open

lalith0407 wants to merge 1 commit into
Egonex-AI:mainfrom
lalith0407:fix/extractor-file-scope-calls

Conversation

@lalith0407

Copy link
Copy Markdown

Summary

Fixes #721 · Refs #367 · Follows the convention introduced in #400 (PHP)

extractCallGraph() in the TypeScript/JavaScript and Python extractors only recorded a call when functionStack.length > 0. Any call outside a named function was silently dropped: module-level wiring (app.use(router), app = Flask(__name__)), if __name__ == "__main__": blocks, top-level main() invocations, IIFEs, and anonymous callbacks at file scope (items.forEach(x => register(x))). Entry-point, bootstrap, and config modules therefore ended up with an empty or near-empty call graph.

Changes

  • <file> caller convention. When no enclosing named function exists, the call is now attributed to the synthetic caller "<file>", the same stable, language-neutral identifier fix(php-extractor): capture file-scope calls + map include/require as imports (#367) #400 uses for PHP. Calls inside named functions are unchanged.
  • FILE_SCOPE_CALLER constant. Added export const FILE_SCOPE_CALLER = "<file>" in plugins/extractors/types.ts and re-exported it from @understand-anything/core, so every extractor (and fix(php-extractor): capture file-scope calls + map include/require as imports (#367) #400's PHP fix, once merged) can share one definition instead of repeating the string.
  • Mapping <file> to the file node. Edges are built by the file-analyzer agent rather than a code-level merge step, so I added a note to agents/file-analyzer.md: a caller of "<file>" means file-scope code; never create a function node named <file>, use the file node (file:<path>) as the edge source.
    Question for maintainers: would you prefer this enforced in code instead (e.g. rewriting <file> callers to the file node id in extract-structure-result.mjs / the merge script) rather than relying on the prompt? Happy to add that in this PR or a follow-up.

Tests

  • TypeScript (5 new): top-level main(); express() / app.use(router) at module scope; calls inside a top-level IIFE; anonymous items.forEach callback; a regression test that calls inside a named function (including nested anonymous callbacks) stay attributed to that function with no <file> entries.
  • Python (4 new): top-level main(); calls under an if __name__ == "__main__": guard; module-level Flask(...) / app.register_blueprint(...); a regression test that a file whose calls are all inside functions emits no <file> entries.
  • Updated: the Python test ignores top-level calls (no caller) encoded the old dropping behaviour. It's renamed to attributes top-level calls to the synthetic <file> caller and now expects two <file> entries (print, main).

Test results (Node 22.23.3, pnpm 10)

  • pnpm --filter @understand-anything/core build: ✅
  • pnpm --filter @understand-anything/core test: ✅ 47 files, 1022 tests passed
  • pnpm test: ✅ 38 files passed, 1 skipped; 786 tests passed, 12 skipped (the skips are pre-existing)
  • pnpm lint: ✅ clean

Notes

  • Scope is deliberately limited to TS/JS and Python. The same functionStack.length > 0 check exists in the Go, Rust, Java, C#, C++, Kotlin, Ruby, Scala and Dart extractors; I can follow up per language.
  • fix(extractor): capture static CommonJS imports and exports #715 also touches typescript-extractor.ts, so there may be a trivial conflict.

… (TS/JS, Python)

extractCallGraph in the TypeScript and Python extractors only recorded a
call when functionStack was non-empty, so module-level calls, __main__
guards, IIFEs and anonymous callbacks at file scope were dropped. Attribute
them to the synthetic "<file>" caller (same convention as Egonex-AI#400 for PHP),
export FILE_SCOPE_CALLER from core, and instruct the file-analyzer to map
"<file>" to the file node rather than a function node.

This branch has not been deployed

No deployments
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.

bug: call graph and structure drop module-level / top-level code in TS/JS, Python (and other non-PHP extractors)

1 participant