feat(parser): populate node.extra.decorators for Function and Class nodes#332
feat(parser): populate node.extra.decorators for Function and Class nodes#332gzenz wants to merge 8 commits into
Conversation
5bc49f7 to
a0b2e5f
Compare
- Remove duplicate macOS-copy module files (analysis 2.py, enrich 2.py, enrich 3.py, exports 2.py, exports 3.py, graph_diff 2.py, jedi_resolver 2.py, memory 2.py, memory 3.py, token_benchmark 2.py) that tripped ruff N999. - Sort imports in main.py (ruff I001). - Drop unused parse_git_diff_ranges import in tools/review.py (ruff F401). - Add type-ignore for FastMCP._tool_manager private attribute access in main.py. CI on main has been red since PR tirth8205#94. This fixes it.
MCP tool calls were opening a fresh GraphStore (and SQLite connection) on every invocation. Cache one GraphStore per db_path and reuse it, falling back to a fresh connection if the cached one is dead. Thread-safe via a module-level lock.
Exact known-answer assertions for compute_risk_score and trace_flows, plus error-path coverage for parser on malformed input and module-cache eviction. - TestRiskScoreExact: 6 tests pinning risk score math (untested, tested, security keyword, caller fractions, 20-caller cap) - TestFlowExact: 4 tests for linear chain depth, cycles, single-file criticality, and non-test neighbors - TestParserErrorPaths: 8 tests for binary files, unknown extensions, syntax errors, empty files, CRLF, deeply nested AST, unicode - TestCacheEviction: oldest-half module cache eviction
Add three helpers: - `_resolve_star_imports`: walks top-level `import_from_statement` nodes, detects `wildcard_import`, resolves the source module to a file, and merges the exported names into the caller's import_map. - `_get_exported_names`: returns the public names a module exports, respecting `__all__` when present. Caches results per resolved path with a threading lock so concurrent parses share work safely. - `_extract_dunder_all`: parses `__all__ = [...]` at module scope. Wires star-import expansion into `parse_bytes` and the notebook concat path. Enables resolution of calls that come in via wildcard imports (e.g. `from constants import *` then `use_constant()`).
a0b2e5f to
a55bd65
Compare
When receiver filtering emits "Foo.bar" (uppercase receiver), resolve it to "/path/file::Foo.bar" by looking up the class entry in the file symbol table. Closes a resolution gap where class-qualified bare calls stayed unresolved after the receiver filter rewrite.
Add _resolve_cross_file_class_methods as a postprocess step that scans Class nodes, builds a name -> file::ClassName map (dropping colliding names), and rewrites bare ClassName.method edge targets to the qualified file::ClassName.method form. Complements the per-file resolver in parser.py which only sees symbols in the calling file.
The cross-file ClassName.method resolver dropped any class whose name appeared in more than one file. That was too strict: codebases commonly reuse short class names across feature packages (util/ClassA.kt and test/ClassA.kt, multiple platform-specific Impl files, etc.). The call site almost always means the version in the closest package. Rebuild class_candidates as a name -> [qn, ...] map. For each edge with a bare ClassName.method target, pick the class qn whose file path shares the deepest common directory prefix with the edge's calling file. If the top match is not unique, fall back to leaving the target bare (rather than mis-resolving).
…odes The parser already extracted decorator tuples for test detection but discarded them before emitting NodeInfo. flows._has_framework_decorator and refactor._is_entry_point expect this data on nodes[...].extra; with no source data, entry-point detection never fires and framework-exposed callables (FastAPI routes, Click commands, Spring handlers) are flagged as dead. Wire class-level decorators (Python decorated_definition, Java/Kotlin modifiers.annotation/marker_annotation) and function-level decorators (existing tuple) into NodeInfo.extra["decorators"]. flows.py patterns already cover FastAPI/Click/Spring/Angular/Celery/pytest.
a55bd65 to
7c21886
Compare
|
Most of head |
|
Final maintainer disposition: the valid Python class-decorator work from this PR has merged through #643 in commit 9cc2458, with Gideon Zenz retained as co-author.\n\nThe replacement preserves stacked class decorators in source order, including combinations such as @component and @DataClass, while leaving undecorated classes and non-Python parsing unchanged. Decorator metadata is read from the enclosing decorated_definition node, stored on the class, and verified end to end through framework/dead-code classification rather than only testing raw parser output.\n\nVerification on the exact merge base:\n- 152 focused parser/refactor tests passed;\n- 1,522 non-daemon tests passed locally, with the macOS daemon file delegated to CI because the third-party FSEvents thread crashes locally;\n- all 11 required GitHub checks passed, including Linux on Python 3.10 through 3.13, lint, type-check, security, schema sync, review, and Windows daemon/file-handle coverage.\n\nNo unresolved part of this source contribution remains. |
Summary
Surface decorator tuples on Function/Class nodes so
flows._has_framework_decoratorandrefactor._is_entry_pointcan fire. Closes the backend/api + backend/cli dead-code regression (HA: 275 → 122).Stacked PR approach
This is PR 7/16 in a stack that ports closed #158 into small, reviewable slices based on your feedback. Each branch contains this commit plus every earlier branch in the stack, so it applies cleanly on its own. The intended merge order is bottom-to-top.
Stack (merge order):
After you merge PR N, I rebase PRs N+1..16 on current main and force-push. Each PR ends up as a single commit on top of the previous one. Review only the newest commit in this PR; the rest will already be on main (or in earlier PRs in the stack).
All 16 branches are rebased on 072ab80. None re-attempts the closed lang-handler refactor.
Roadmap: restoring v7 loop-test parity
Measured impact on loop-test fixtures (Gadgetbridge Kotlin/Java, HealthAgent Python/TS):
Still deferred after this stack