Skip to content

fix(resolution): a definition its language makes file-local is not a cross-file target (#1731) - #1745

Merged
colbymchenry merged 3 commits into
mainfrom
forge/fix-1731-cross-file-visibility
Sep 8, 2026
Merged

colbymchenry merged 3 commits into
mainfrom
forge/fix-1731-cross-file-visibility

Conversation

@colbymchenry

Copy link
Copy Markdown
Owner

Summary

Rebases and lands PR #1732 (danusha2345) onto latest main (post-#1742/#1743), with a small changelog tweak citing #1731 and clarifying the C source-vs-header rule.

Fixes #1731 (Kotlin / Go / Rust). Also covers #1730 (C static in another translation unit).

Name matching accepted any same-named definition as a cross-file target. isVisibleAcrossFiles now declines a candidate in another file when:

language rule
C / C++ function defined in a source file (.c/.cc/…) whose definition carries static — header static inline stays visible
Kotlin, Java, C#, Swift, Scala, Dart, PHP visibility === 'private'
Go lowercase identifier and a different directory (by name case — extractor isExported is unset for methods; see #1734)
Rust non-pub and the ref is outside the item's module subtree; child→ancestor and impl Trait for Type methods stay resolvable

Applied in ReferenceResolver after matchReference settles (and inside matchFuzzy), so rejection leaves the ref unresolved instead of falling through to a fuzzy namesake.

Credits: implementation from #1732 by @danusha2345 / Claude.

Test plan

  • Linux repro on main: Kotlin editor.apply() → private apply, Go cross-package fail, Rust sibling private count all wrongly resolved
  • Same fixtures on this branch: all three declined (KOTLIN_CALLEES [], GO_CALLEES [], RUST_SIBLING_CALLEES [])
  • CODEGRAPH_KERNEL=0 npx vitest run __tests__/cross-file-visibility.test.ts — 11 passed (Node 22.19)
  • CODEGRAPH_KERNEL=0 npx vitest run __tests__/resolution.test.ts — 189 passed

danusha2345 and others added 3 commits September 8, 2026 04:56
…cross-file target

Name matching accepted any same-named definition as the target of a call
from another file, however the language scopes it. isVisibleAcrossFiles
now declines, for a candidate in another file:

  C / C++   a function whose definition line carries `static` (read from
            source — the extractor records no storage class, and the kernel
            arm would need the same field)
  Kotlin, Java, C#, Swift, Scala, Dart, PHP
            visibility === 'private'
  Go        a lowercase identifier from another directory (by the name's
            case: the extractor's isExported is unset for every Go method)
  Rust      a non-`pub` item unless the reference is in the item's module
            subtree (a child sees its ancestors' private items via super::);
            a method in an `impl Trait for Type` block has the trait's
            visibility and is exempt

The test runs in ReferenceResolver on the target the whole name-matching
pipeline settled on, so a rejection ends the reference unresolved. Declining
inside matchByExactName instead let the ref fall through to matchFuzzy,
which committed to a same-language namesake the ranking had passed over —
eight edges on one tree, all onto a local `const fail = …` arrow the graph
does not hold. matchFuzzy checks its own survivor too; nothing runs after it.

Five corpora, all against b9ca4b7, wasm arm, edge rows keyed with
resolvedBy:

  betaflight fork (2,109 C files)        LOST 4,451  GAINED 0   (#1730)
  Android/Go/JS app (114 kt, 42 go)      LOST   142  GAINED 0   (#1731)
  emmc-reader-gui (71 rs)                LOST   195  GAINED 0
  skylab_hub (35 rs)                     LOST    92  GAINED 0
  vitejs/vite (JS/TS only)               LOST     0  GAINED 0

Samples read back: `Vec::new()` onto a private `fn new` in another crate,
`ui.add(…)` (egui) onto a private `add`, `latch.await()` onto a test file's
`private fun await`, `leaflet.js` onto an unexported Go `func add`,
`usbd_get_descriptor` onto a `static get_device_descriptor` in a USB class
file it never links.

Fixes #1730. Fixes #1731.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…udes it

The C rule declined any `static` function defined in another file. A
`static` in a SOURCE file is local to that translation unit and the rule is
right there; a `static` in a header — `static inline`, the whole of
MAVLink's generated `mavlink_msg_*.h` — is textually included into every
unit that names it, and the call is real. On the betaflight tree 4,306 of
the 4,451 rows the first cut removed were exactly that: `testsuite.h` and
`mavlink_msg_*.h` calling `protocol.h`'s `_mav_put_char_array`,
`mav_array_assign_char` and each other's `_pack` / `_decode` helpers.

The rule now applies only to a candidate whose file is a translation unit
(`.c .cc .cpp .cxx .c++ .m .mm`). Same tree: LOST 145, GAINED 0, every one
onto a `static` in another `.c` — STM32 USB class sources onto GD32's
`usbd_enum.c`, and the USB descriptor table shape from #1730. Header
targets in the removed set: 0.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Rebased #1732 onto latest main. Clarify that only a static in another
source file is declined (header static inline stays), name the Kotlin/Go/Rust
shapes from #1731, and note the post-pipeline placement that avoids fuzzy
fallback.
@colbymchenry

Copy link
Copy Markdown
Owner Author

Related open PR #1734 fixes Go method isExported in the extractor; this PR intentionally judges Go by name case so it does not depend on #1734. Once #1734 lands the flag and case agree.

@colbymchenry
colbymchenry merged commit 2c251e2 into main Sep 8, 2026
@colbymchenry
colbymchenry deleted the forge/fix-1731-cross-file-visibility branch September 8, 2026 05:00
bompus added a commit to bompus/codegraph that referenced this pull request Sep 8, 2026
Upstream re-landed four of the fork's changes under its own commits today
(colbymchenry#1697 directly; colbymchenry#1766, colbymchenry#1767, colbymchenry#1769 rebased from the closed colbymchenry#1710, colbymchenry#1706,
colbymchenry#1695), so the fork carried a second copy of each and the hourly sync stopped
with nine conflicts. Upstream's version wins wherever it now owns the change.

take upstream, drop the fork's duplicate:
  src/bin/codegraph.ts        capPromptHookInjection() supersedes the inline
                              MAX = 9000 the fork carried from colbymchenry#1695.
  src/extraction/tree-sitter.ts  CommonJS export assignment support (colbymchenry#1675);
                              the fork had no version of it.
  src/resolution/name-matcher.ts  comment-only conflict; upstream's text
                              already covers colbymchenry#1745, colbymchenry#1719, colbymchenry#1714, colbymchenry#1230,
                              colbymchenry#1708 and colbymchenry#1709.

keep the fork's version, upstream has none:
  src/mcp/tools.ts            the codegraph_sessions tool definition (colbymchenry#1702).
  README.md                   documents that alwaysLoad also covers
                              codegraph_sessions.
  __tests__/fixtures/kernel-parity/torture.tsx   markdown-path fixture.

union, upstream entry first:
  CHANGELOG.md                per UNION_MERGE_FILES.
  __tests__/fixtures/kernel-parity/torture.js    the fork's colbymchenry#693 initializer
                              walks and upstream's colbymchenry#1675 CommonJS exports are
                              different fixtures at the same point in the file.
  __tests__/fuzzy-lexical-reach.test.ts   add/add: both sides created it.
                              Upstream's C-nesting test plus the fork's two
                              sealed-module tests, with the one test both
                              sides wrote kept once.

Verified on Windows against a release build of the kernel: tsc --noEmit
clean, npm run build clean (engine, UI, 29 wasm grammars), full native suite
4438 passed of 4483. The single failure is a refresh-launcher timeout that
passes in 2.7s when the file is run on its own.
danusha2345 pushed a commit to danusha2345/codegraph that referenced this pull request Sep 30, 2026
`private val` / `private var` at class or object scope were indexed with
no visibility, so colbymchenry#1745's Kotlin rule — which declines a `private fun`
in another file — read every property as public. On a Kotlin + JS + Go
app 24 cross-file calls landed on private properties: a validator's
`token(...)` onto another class's `@Volatile private var token`, and JS,
Go and Python callers onto Kotlin test fixtures' private fields.

Both extractors read the `modifiers` child the way functions already
do; the parity fixture gains a `private val` and an `internal var`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
danusha2345 pushed a commit to danusha2345/codegraph that referenced this pull request Oct 1, 2026
`private val` / `private var` at class or object scope were indexed with
no visibility, so colbymchenry#1745's Kotlin rule — which declines a `private fun`
in another file — read every property as public. On a Kotlin + JS + Go
app 24 cross-file calls landed on private properties: a validator's
`token(...)` onto another class's `@Volatile private var token`, and JS,
Go and Python callers onto Kotlin test fixtures' private fields.

Both extractors read the `modifiers` child the way functions already
do; the parity fixture gains a `private val` and an `internal var`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
danusha2345 pushed a commit to danusha2345/codegraph that referenced this pull request Oct 6, 2026
`private val` / `private var` at class or object scope were indexed with
no visibility, so colbymchenry#1745's Kotlin rule — which declines a `private fun`
in another file — read every property as public. On a Kotlin + JS + Go
app 24 cross-file calls landed on private properties: a validator's
`token(...)` onto another class's `@Volatile private var token`, and JS,
Go and Python callers onto Kotlin test fixtures' private fields.

Both extractors read the `modifiers` child the way functions already
do; the parity fixture gains a `private val` and an `internal var`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
danusha2345 pushed a commit to danusha2345/codegraph that referenced this pull request Oct 7, 2026
`private val` / `private var` at class or object scope were indexed with
no visibility, so colbymchenry#1745's Kotlin rule — which declines a `private fun`
in another file — read every property as public. On a Kotlin + JS + Go
app 24 cross-file calls landed on private properties: a validator's
`token(...)` onto another class's `@Volatile private var token`, and JS,
Go and Python callers onto Kotlin test fixtures' private fields.

Both extractors read the `modifiers` child the way functions already
do; the parity fixture gains a `private val` and an `internal var`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
danusha2345 pushed a commit to danusha2345/codegraph that referenced this pull request Oct 7, 2026
`private val` / `private var` at class or object scope were indexed with
no visibility, so colbymchenry#1745's Kotlin rule — which declines a `private fun`
in another file — read every property as public. On a Kotlin + JS + Go
app 24 cross-file calls landed on private properties: a validator's
`token(...)` onto another class's `@Volatile private var token`, and JS,
Go and Python callers onto Kotlin test fixtures' private fields.

Both extractors read the `modifiers` child the way functions already
do; the parity fixture gains a `private val` and an `internal var`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
danusha2345 pushed a commit to danusha2345/codegraph that referenced this pull request Oct 10, 2026
`private val` / `private var` at class or object scope were indexed with
no visibility, so colbymchenry#1745's Kotlin rule — which declines a `private fun`
in another file — read every property as public. On a Kotlin + JS + Go
app 24 cross-file calls landed on private properties: a validator's
`token(...)` onto another class's `@Volatile private var token`, and JS,
Go and Python callers onto Kotlin test fixtures' private fields.

Both extractors read the `modifiers` child the way functions already
do; the parity fixture gains a `private val` and an `internal var`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
danusha2345 pushed a commit to danusha2345/codegraph that referenced this pull request Oct 15, 2026
`private val` / `private var` at class or object scope were indexed with
no visibility, so colbymchenry#1745's Kotlin rule — which declines a `private fun`
in another file — read every property as public. On a Kotlin + JS + Go
app 24 cross-file calls landed on private properties: a validator's
`token(...)` onto another class's `@Volatile private var token`, and JS,
Go and Python callers onto Kotlin test fixtures' private fields.

Both extractors read the `modifiers` child the way functions already
do; the parity fixture gains a `private val` and an `internal var`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
danusha2345 pushed a commit to danusha2345/codegraph that referenced this pull request Oct 19, 2026
`private val` / `private var` at class or object scope were indexed with
no visibility, so colbymchenry#1745's Kotlin rule — which declines a `private fun`
in another file — read every property as public. On a Kotlin + JS + Go
app 24 cross-file calls landed on private properties: a validator's
`token(...)` onto another class's `@Volatile private var token`, and JS,
Go and Python callers onto Kotlin test fixtures' private fields.

Both extractors read the `modifiers` child the way functions already
do; the parity fixture gains a `private val` and an `internal var`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
danusha2345 pushed a commit to danusha2345/codegraph that referenced this pull request Oct 20, 2026
`private val` / `private var` at class or object scope were indexed with
no visibility, so colbymchenry#1745's Kotlin rule — which declines a `private fun`
in another file — read every property as public. On a Kotlin + JS + Go
app 24 cross-file calls landed on private properties: a validator's
`token(...)` onto another class's `@Volatile private var token`, and JS,
Go and Python callers onto Kotlin test fixtures' private fields.

Both extractors read the `modifiers` child the way functions already
do; the parity fixture gains a `private val` and an `internal var`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
danusha2345 pushed a commit to danusha2345/codegraph that referenced this pull request Oct 21, 2026
`private val` / `private var` at class or object scope were indexed with
no visibility, so colbymchenry#1745's Kotlin rule — which declines a `private fun`
in another file — read every property as public. On a Kotlin + JS + Go
app 24 cross-file calls landed on private properties: a validator's
`token(...)` onto another class's `@Volatile private var token`, and JS,
Go and Python callers onto Kotlin test fixtures' private fields.

Both extractors read the `modifiers` child the way functions already
do; the parity fixture gains a `private val` and an `internal var`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
danusha2345 pushed a commit to danusha2345/codegraph that referenced this pull request Oct 21, 2026
`private val` / `private var` at class or object scope were indexed with
no visibility, so colbymchenry#1745's Kotlin rule — which declines a `private fun`
in another file — read every property as public. On a Kotlin + JS + Go
app 24 cross-file calls landed on private properties: a validator's
`token(...)` onto another class's `@Volatile private var token`, and JS,
Go and Python callers onto Kotlin test fixtures' private fields.

Both extractors read the `modifiers` child the way functions already
do; the parity fixture gains a `private val` and an `internal var`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
danusha2345 pushed a commit to danusha2345/codegraph that referenced this pull request Oct 22, 2026
`private val` / `private var` at class or object scope were indexed with
no visibility, so colbymchenry#1745's Kotlin rule — which declines a `private fun`
in another file — read every property as public. On a Kotlin + JS + Go
app 24 cross-file calls landed on private properties: a validator's
`token(...)` onto another class's `@Volatile private var token`, and JS,
Go and Python callers onto Kotlin test fixtures' private fields.

Both extractors read the `modifiers` child the way functions already
do; the parity fixture gains a `private val` and an `internal var`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
colbymchenry added a commit that referenced this pull request Oct 23, 2026
…y link again (#2832)

Dart keeps a `_name` to its library: the library file and every file its
`part` directives name. isVisibleAcrossFiles still listed Dart in
PRIVATE_IS_FILE_LOCAL (added with PR #1745 for Kotlin, Java, C#, Swift,
Scala and PHP), and both Dart extractors mark a `_` function, method or
class `private`. So once the name-matching pipeline had picked a private
candidate in another file, the pick was declined and the reference stayed
unresolved. A library split into parts, generated `.g.dart` and
`.freezed.dart` parts included, lost the calls and type uses between its
files.

Fix:
- dartPrivateVisible (dart-libraries.ts) says whether a file belongs to the
  reference's library, read from `part` / `part of` alone. Unlike
  inSameDartLibrary it needs no pubspec.yaml, since a part names its library
  by a path or a library name. When the reference's library can't be found,
  the name stays file-local, as before.
- isVisibleAcrossFiles takes Dart out of PRIVATE_IS_FILE_LOCAL and lets a
  private Dart candidate through when it is in the reference's library.
- matchByExactName drops another library's private Dart candidates before
  the ranking, as it does for C#, PHP, Java and Go names the file can't see.
  Otherwise another library's namesake can rank first, be declined, and take
  the library's own declaration down with it. Without a pubspec nothing else
  filters a top-level `_name`, and a member's class hierarchy is matched by
  type name, which can reach another library's class of the same name (the
  SDK's wasm and VM `_ImmutableLinkedHashMapMixin`).

Validation:
- New __tests__/dart-part-privacy.test.ts, run with and without a pubspec:
  the issue's library plus a receiver call, a generated part's
  `_$UserFromJson` and `_$Counter`, another library that imports the one
  declaring the names, a namesake beside a part, a part named by library
  name, and a part whose library is missing. 6 of 12 fail on main's
  resolver, 12 pass. With the pre-ranking filter disabled, 1 fails (the
  namesake without a pubspec).
- A/B vs main 58eb57a, each arm with its own dist and kernel; the kernel
  and wasm paths give the same changed-site sets. Re-run after the rebases,
  on 5b47bc4 (kernel) and 8fdc8a1 (wasm), with the same changed sites.
  Edges added: getx (217 Dart files) 0, bloc (616) +49, riverpod (1,300)
  +542, localsend (393) +1, protobuf.dart (225) +143, apidash (954) +342,
  dart-lang/sdk sdk/lib (480, no pubspec) +207. Nothing removed or
  retargeted, and 1,064 edges changed only their confidence. Every newly
  resolved ref has its edge at the same site; nodes and files are
  unchanged.
- Triage of all 1,284 added edges: 1,274 right. An independent
  part/part-of oracle confirms 1,195 (a top-level `_name` unique in its
  library, a member of the call site's own hierarchy, or a member of the
  receiver's declared type); 79 were checked by hand. 10 are wrong, all
  receiver-name guesses (instance-method 0.65-0.8) that pick one
  implementation of an abstract member, or the first of a tie: the guess
  public names already get, which the file-local rule had hidden. The
  1,064 confidence changes are same-file targets that had another
  library's namesake beside them (exact-match 0.7 to 0.9).
- gin and moshi controls byte-identical, kernel and wasm. Kernel and wasm
  full-index dumps byte-identical on every Dart repo. kernel-parity --lang
  dart: 0 diffs on all seven repos.
- Index time, median of 3 interleaved runs on the kernel path, CPU with
  workers, main vs fix: riverpod 55.6 / 50.3 s, sdk/lib 59.4 / 59.6 s,
  apidash 29.5 / 30.8 s, bloc 18.6 / 18.3 s, protobuf.dart 14.7 / 14.3 s.
  Within noise.

Fixes #2466

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
danusha2345 pushed a commit to danusha2345/codegraph that referenced this pull request Oct 23, 2026
`private val` / `private var` at class or object scope were indexed with
no visibility, so colbymchenry#1745's Kotlin rule — which declines a `private fun`
in another file — read every property as public. On a Kotlin + JS + Go
app 24 cross-file calls landed on private properties: a validator's
`token(...)` onto another class's `@Volatile private var token`, and JS,
Go and Python callers onto Kotlin test fixtures' private fields.

Both extractors read the `modifiers` child the way functions already
do; the parity fixture gains a `private val` and an `internal var`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.

Kotlin, Go, Rust: a private / unexported / non-pub definition is accepted as a cross-file name-match target

1 participant