Commit Graph
2 Commits
Author SHA1 Message Date
HemanthandClaude Opus 5.5 03b420df97 fix(python): salt module-level class ids that collide with a function or class (#2810)
The Python collision pre-scan (#3302) recorded module-level functions and
class methods but not module-level class names, so a class sharing an id
with a function or another class after normalization still lost one
declaration to first-wins add_node:

- `class Session` / `def session()` (requests/sessions.py): session() is
  absent from the graph, and which one survives depends on declaration order
- `class Foo` / `class FOO`, `class Pair` / `class _Pair`

Record module-level classes in the same scope as functions and salt the
class id with the existing _underscore_salted_nid rule. Methods of a salted
class are minted under the salted id, so the pre-scan now keys their groups
under the class's resolved id; keying them under the plain one would drop
the existing `_get`/`get` protection inside that class. Same-file base-class
references go through the same salt (as C# already does), so
`class Sub(Session)` binds to the real class instead of minting a sourceless
`Session` stub. The pre-scan also looks through decorated_definition, so
`@dataclass class Session` and the `_x` / `@property x` method idiom are
covered too.

Ids change only for files that contain such a collision; cached AST results
for them keep the collapsed ids until the AST cache schema is bumped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 14:02:26 +05:30
ayushcodes10 343f76f6ec Fix Python leading-underscore method/function id collision
ids.py:make_id strips leading/trailing underscores from every name
part before normalizing, so _get_connection/get_connection (and more
broadly any name pair differing only by underscore wrapping --
x/_x/__x/__x__ dunder variants all collapse the same way) mint the
SAME node id. add_node's first-wins dedup then silently drops
whichever declaration is walked second: a public method could be
entirely absent from the graph while its private-by-convention
sibling occupied the public name, with no warning and exit 0.

Adds a Python-scoped pre-scan (_python_pre_scan_underscore_collisions)
that groups module-level functions and direct class methods by their
would-be node id, plus a resolver (_python_underscore_salted_nid) that
salts every group member except a unique public (no leading
underscore) one -- mirroring the established convention the Go
extractor already uses for its own case-only collision (#2779/#2780):
when no unique public member exists, every member is salted, so the
outcome never depends on declaration order. The public member's id
stays exactly what it would be with no colliding sibling at all, so
adding/removing a private-by-convention sibling in an incremental
rebuild re-points nothing.

Both hooks are gated on config.ts_module == "tree_sitter_python" and
live inside the shared _extract_generic engine (Python has no
dedicated extractor file, unlike Go/Rust/SQL), following the existing
precedent for per-language pre-scans already there (csharp_interface_names,
swift_protocol_names). Call-site resolution needed no changes: label_to_nid
is rebuilt from the final nodes list after the walk completes, so it
naturally picks up whichever id (salted or not) a method actually got.

9 new tests in tests/test_python_underscore_resolution.py: the exact
repro, id stability across incremental add/remove of a private
sibling, the call edge correctly binding to the salted target, the
module-level (non-method) case, the no-unique-public-member case
(both members salted), a dunder+plain collision, an unaffected
no-collision file, and scope isolation (a collision in one class must
not touch an unrelated same-named-but-non-colliding member in another
class).

Note: tests/ on the current v8 HEAD has 7 pre-existing failures
unrelated to this change (test_cli_export.py, test_cross_repo_shared_types.py,
test_extract_code_only_cli.py, test_merge_graphs_cli.py) -- confirmed via
`git stash` that they fail identically on pristine v8 without this diff.
Full suite otherwise: 5116 passed. ruff and skillgen --check clean.

Fixes #3302.

(cherry picked from commit cb8ccebab9)
2026-09-05 21:55:32 +01:00