Commit Graph

1242 Commits

Author SHA1 Message Date
Colby Mchenry fa8d7284c5 fix(rust): a bare name is in scope only through its module or a use (#2214)
The Rust scope rule (#2124) only checked prelude names; every other bare
name could mean any same-named project item, and a name after any path
(`io::Result`) could mean any at all. The Rust framework resolver's
PascalCase / handler / service patterns bound names with no scope check at
0.7-0.8. tokio's 1,221 `io::Result<...>` went to a private
`runtime::task::Result` alias, 642 `Context<'_>` (from `use
std::task::{Context, Poll}`) to `runtime::context::Context`.

- A type or function from another file is in scope only through a `use`
  that binds it (its leaf, `as` alias, or `{self}`) or a glob over its
  module; methods and fields (reached through a value), import refs and
  module / file targets are unaffected.
- A name a file imports from outside the project - `std` / `core` /
  `alloc`, or a crate its Cargo manifests declare as a dependency - is that
  outside item. (An inline `mod support { pub mod panic; }` is the
  project's.)
- A path names its module: `seg::Name` reaches an item of module `seg`;
  a path rooted at std (`std::io::Error`) or at an outside import (`io::`
  under `use std::io`) reaches none; `crate::` / `self::` / `super::` and a
  project crate's name (`clap::Command`, re-exported) look it up as before.
  The path is read anywhere on the line, not only at the reference's column.
- The framework resolver's patterns apply the same check.

tokio -3008 +610, serde -792 +650, ripgrep -602 +247, axum -453 +186, clap
-342 +32, bat -38 +9. Sampled losses: std / core / futures / syn / http /
anyhow imports and paths, associated types (`S::Ok`, `S::Error`, `V::Value`,
`Fut::Output`); gains are same-module items the old first match hid.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 22:54:08 +00:00
Colby Mchenry 5f1a635714 fix(resolution): any call reaches the overload its arguments fit (#2213)
#2212 moved a method's call to its own name onto the sibling overload the
arguments fit. The same holds for every caller: a match landing on an
overload whose parameters cannot take the call's arguments moves to the one
same-owner overload they fit - commons-lang's
`HashCodeBuilder.reflectionHashCode(this)` from the three-parameter overload
indexed first to the `(Object, String...)` one, Newtonsoft's
`SetToken(token, value)` from `SetToken(token)`, halo's four-argument
`menuItem(...)` from the one-argument `menuItem`.

Overload sets and arities are read once per target node, and a target with
no same-owner sibling costs one lookup, so indexing time is unchanged
(commons-lang 4.67s vs 4.70s, Newtonsoft 4.04s vs 4.05s, wasm both arms).
C++ keeps only the self-call case: its overload sets (templates, SFINAE
priority tags, `data()` on any container) move targets unreliably.

Targets moved between same-named overloads: commons-lang 2,334, Newtonsoft
697, halo 286, gson 232, okhttp 188, Alamofire 50; nlohmann json and fmt
unchanged. Edge counts unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 22:32:43 +00:00
Colby Mchenry c20283e2ce fix(resolution): a call to an overload of the calling method reaches it (#2212)
A method that delegates to another overload of itself -
`toInstant(instant)` returning `toInstant(instant, Instant.EPOCH)`,
`DeserializeXNode(value)` returning `DeserializeXNode(value, null)` - bound to
itself, since every overload shares the name: the fuller overload never
listed its convenience overloads among its callers, and its impact missed
them.

When a name match lands on the calling method itself, in a language that
overloads by arity (C#, Java, Kotlin, Swift, C++, Scala, Dart, VB.NET), the
call's argument count is read and compared with the method's parameters
(defaults and varargs / `params` / `...` counted); if they cannot take it,
the one same-owner overload they fit is the target. Real recursion
(`depth(n - 1)`) keeps its self-edge.

Self-loops -> overload edges: commons-lang 637 -> 457, Newtonsoft 303 -> 196,
halo 173 -> 116, okhttp 248 -> 208, Alamofire 147 -> 120, gson 74 -> 55,
AutoMapper 71 -> 57 (edge counts unchanged; overloads share a qualified
name, so only the target moves).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 22:21:17 +00:00
Colby Mchenry 5c01f017cf fix(react): the React resolver only resolves a script's references (#2211)
The React resolver's `languages` gates its route extraction, not its
resolve(): framework resolvers see every language's references. Its
component rule was already JSX-only, but its hook (`use...`) and context
(`...Context` / `...Provider`) rules were not: in halo - a Spring app whose
UI package makes React detected - 209 Java import edges (`SecurityContext`,
`ApplicationContext`, `ObjectProvider`, `DirtiesContext`...) were resolved by
it onto import nodes. It now answers only JavaScript / TypeScript / TSX /
JSX references.

halo -209 +3 (a Java test's `responseContext()` helper now resolves to
itself); trpc, excalidraw, react-redux-realworld, bulletproof-react,
react-native-reusables unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 22:10:14 +00:00
Colby Mchenry d7cdb0c09b fix(vue): the Vue resolver only resolves a Vue app's scripts (#2210)
Framework resolvers see every reference of every language, and the Vue
resolver had no language guard: its PascalCase-component rule took
mealie's Python `QueryFilterBuilder(...)` (an unresolved import) to the
`QueryFilterBuilder.vue` component at 0.8, and its macro / auto-import rules
matched any language's `ref(...)` or `computed(...)`. It now answers only
refs from vue / javascript / typescript / tsx / jsx files.

mealie: 6 Python calls reroute to the Python class; halo, elk, element-plus
unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 22:01:39 +00:00
Colby Mchenry de9f346ceb fix(resolution): production code never names a test suite's symbol (#2209)
A test suite is not linked into the program, so a name match from
production code into one is coincidence - but only Strategy 3's receiver
guess (#2171) ruled it out. Now no strategy's name match reaches a symbol
in a test suite from a non-test file: a test source set (`src/test/`,
`jvmTest/`), a `tests/` / `__tests__/` / `spec/` / `e2e/` directory, a
`FooTest.kt` / `test_foo.py` / `foo.test.ts` / `foo_spec.rb` file.
Test-support code a project ships - `testing/`, `fakes/`, `mocks/`, a
`*-test` module - stays in reach, and `…Spec.java` (halo's `IndexSpecs`,
okhttp's `ConnectionSpec`), `post-spec.ts` models and `useTests.ts` hooks
are not test suites.

typeorm -64 (`Record<K, V>` -> a test entity), okhttp -164 (117 sample
`@Override` -> a test's nested `Override`), kotlinx.coroutines -318
(`import kotlinx.coroutines.*` onto one test file's package node), sttp -62,
Exposed -92, halo 0, element-plus -36 (`isClient` -> a test's vueuse mock),
excalidraw -10 (`actionManager.executeAction` -> the test `API` helper),
tokio -30, trpc -36, AutoMapper -37, netbox -22, cats -21, mealie -10,
pytest -8, Alamofire -7; zod, eShop, react-native-reusables unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 21:52:10 +00:00
Colby Mchenry b8f2bd513e test(ui-server): time the busiest symbol as the best of five requests (#2208)
`answers in under 100 ms` took one wall-clock sample while the rest of the
suite ran in parallel, and failed under that load at 250-430 ms (three
times in one day of full-suite runs) while passing alone. It now takes the
fastest of five warm requests: contention is gone from at least one, and a
real slowdown is slow on every one.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 21:34:36 +00:00
Colby Mchenry eb1452ba82 fix(dart): core method names on untyped receivers are the core type's (#2207)
Dart joins Rust/Go/Kotlin/C# with a standard-method list (String, List,
Iterable, Map, Set: endsWith, contains, forEach, putIfAbsent, where,
sublist...): a guess onto a project method of such a name needs a receiver
named after its owner. And a Dart extension's owner, for that naming, is the
type it is `on` - getx's `ext.endsWith(".avi")` on a String shared the word
`Ext` with `extension RxStringExt on Rx<String>`, none with `Rx<String>`.

getx -36 (`RxStringExt::endsWith` x23, IOHttpResponse::join/toList),
riverpod -37 (`UnmodifiableMap::putIfAbsent/addAll` on plain maps), bloc -18
(`ignore.contains(...)` -> GithubCache, a Map's `forEach` -> _Emitter; bloc's
untyped `emit.forEach(...)` handler lambdas lose their lucky guess too).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 21:31:14 +00:00
Colby Mchenry fc73204f12 fix(kotlin): a DSL lambda runs on its own receiver (#2206)
#2172 made the members of every type a project function type takes as a
receiver reachable from any bare call, since it could not tell which
lambda a call is in. With several such types in reach, proximity chose:
koin's `single { C51(get(), get()) }` and `Scope.new(...)`'s `get()` went to
`Koin.get` (koin also has `Koin.() -> ...` lambdas), 2,190 times.

- A lambda's receiver is read from the function it is passed to: the type
  before `.(` in its last parameter's function type, directly or through a
  typealias (`definition: Definition<T>`, `typealias Definition<T> =
  Scope.(ParametersHolder) -> T`), when every project function of that name
  agrees. The brace frames put it in the lambda's hierarchy.
- Of the members a bare call can reach, those its lexical hierarchy reaches -
  its class, an extension's receiver, the lambda it is in - come before those
  only the project-wide receiver set reaches.

koin: `get()` -> Scope (2,184), `viewModel` in `scope { }` -> ScopeDSL,
`modules(...)` in `startKoin { }` -> KoinApplication. Exposed: `varchar(...)`,
`integer`, `reference`, `datetime` in a table object -> Table's (were a
test table's same-named property). kotlinx.coroutines: `launch { }` ->
CoroutineScope.launch (was a test DSL's). Edge counts ~unchanged (targets
move): koin, Exposed, okhttp, coroutines, okio; nowinandroid, moshi
unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 21:25:06 +00:00
Colby Mchenry a329df883a fix(swift): a call on a typed property is a member of its type (#2205)
The declared-member receiver inference #2171 gave C#, Java and Kotlin now
reads Swift too: `var cache: ImageCache!`, `let storage: DiskStorage.Backend<Data>`,
`self.x` as well as `x`, a function's own `let` shadowing the property. The
dotted-call path types the receiver from it, and a chained bare call keeps
only members of that type or its supertypes / protocols.

Kingfisher: `cache.imageCachedType(forKey:)` -> ImageCache's (was a test
subclass's override), `storage.store/value/isCached` -> DiskStorage.Backend,
`manager.retrieveImage` -> KingfisherManager (was a test double), DispatchQueue
`ioQueue.async` no longer a project method. realm-swift: `List::append`,
`RealmCollection::sum/index` (were Objective-C RLMArray selectors); vapor:
`self.storage.write` -> NIOHTTPBodyWriterStorage.

Kingfisher -100 +179, realm-swift -108 +203, Alamofire -16 +54, vapor -15
+10, IceCubesApp -6 +5; Newtonsoft.Json, okhttp unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 21:15:33 +00:00
Colby Mchenry e7be363f95 fix(js): no call resolves by name into a minified bundle (#2204)
A vendored minified script (`*.min.js`, or a script of a few enormous lines
- the same detection #2122 uses to rank them last) keeps mangled one-letter
names, so a name match from any other file is coincidence: healthchecks'
369 `$(...)` jQuery calls went to a helper inside bootstrap-native.min.js,
TestBox's popper.min.js to jquery.min.js internals, flaskbb's docs script
to its vendors bundle. Such files' symbols are no longer visible across
files by name; calls inside a bundle still resolve.

healthchecks -460, flaskbb -48, TestBox -36; bat, lodash, minimal-mistakes,
netbox, express, excalidraw unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 21:09:20 +00:00
Colby Mchenry 4c381aedbd fix(swift): a call on a constructed value is that type's member (#2203)
A chained Swift call accepted any same-named member unless its name was a
standard-library one: vapor's `JSONDecoder().decode(...)` and even its own
`URLQueryDecoder().decode(...)` went to `ClientRequest._URLQueryContainer
.decode`, IceCubes' `JSONEncoder().encode(self)` to a filter's `encode`,
realm's `Realm().create(...)` to a test object's `create`.

When the link before the call is a constructor - `TypeName(...)` with flat
arguments - the member must belong to that type or a supertype / protocol
the project gives it; an outside type (JSONDecoder) keeps only the
project's extensions of it. A capitalized C function
(`RLMObjectBaseObjectSchema(obj)!`) constructs nothing and is left alone.

vapor -63 +62 (`URLQueryDecoder::decode`, `URLEncodedFormParser::parse`),
IceCubesApp -23 +1, realm-swift -19 +19 (`Realm::create`), Alamofire -1;
Kingfisher unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 21:03:23 +00:00
Colby Mchenry b55836daa3 fix(lua): a bare call is never a table method; Lua's globals are Lua's (#2202)
- A bare Lua call cannot reach a table's method (`function M.x`,
  `function M:x`) without its table: kong's 118 `error(...)` calls went to
  its response PDK's `_RESPONSE.error`, 111 to a mock's `error`, busted's
  `setup`/`teardown` to perf helpers' and factories'.
- A bare call to a Lua global (`ipairs`, `error`, `require`, busted's
  `describe`/`it`/`setup`...) is Lua's unless the file defines its own
  function of that name: telescope's 120 `for _, v in ipairs(...)` went to a
  linked list's `ipairs` method, apisix's 36 `require(...)` to a wasm
  module's `_M.require`.
- A function's own locals are not nodes, so the alias reader now reads
  `local x = ...` from source above the call within its function, and a
  member of a host global table (`kong.request.get_query`) resolves to the
  one method of a table named after its holder (`_REQUEST`). kong's
  `local clear_header = kong.service.request.clear_header` had resolved to
  the RESPONSE module's - now nothing, correctly.

telescope -129, kong -516 +58 (indexing 4.7s -> 3.5s), apisix -57 +18,
lua-resty-core -4, roact -3 +1.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 20:46:25 +00:00
Colby Mchenry 116feb462d fix(cpp): pick the overload a macro-namespaced call's arguments fit (#2201)
`fmt::format("{}", x)` resolves through the macro-namespace matcher
(#2176), which took the first same-named function of the namespace -
color.h's `format(const text_style&, format_string, T&&...)` - for all of
fmt's 1,400 `fmt::format` calls, and likewise `print`, `format_to`,
`vformat`.

Among an overload set the call's arguments are now read (to a dozen lines)
and each overload's parameter list from source (C++ signatures are empty):
an argument count outside the parameters' is out; a string-literal argument
fits a parameter that names a string type best, a template parameter
(`const S&`) less, anything else not; a narrow literal never fits a wide
parameter (`wformat_string`, `wstring_view`) nor a wide one a narrow one.
`...` inside `format_string<T...>` is not a pack.

fmt: `format` -> format.h (1,363) and xchar.h for `L"..."` (57),
`format_to`/`print` -> core.h, `vformat` -> format-inl.h; spdlog's bundled
fmt the same; pybind11's `py::set()` -> pytypes.h's `set` class. json,
rapidjson, leveldb unchanged. Edge counts unchanged (targets only).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 20:36:51 +00:00
Colby Mchenry 7a1186471e fix(routes): inline handler calls keep their receivers (#2200)
The Express-style route extractor attributed an inline arrow handler's calls
to the route by bare name - `c.text('Hello')` became `text` - so each
matched whatever same-named symbol the project had: hono's 419 `c.text(...)`
routes called its client's `ClientResponse.text`, `c.html(...)` its `html`
helper, `c.req.parseBody()` a utility `parseBody`. A member call now keeps
its receiver (`c.text`, `userService.lookup`) and resolves like any member
call; a member of an expression (`a.b().c(`) names nothing to follow. Both
the plain and the `router.route(...).get(...)` forms share the scanner.

hono -538 +71 (`Context::text` / `Context::html` where the receiver
resolves; `c.req.*` 3-link chains now stay unresolved like any other
untyped chain); express, react-redux-realworld, trpc, kit, BookStack
unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 20:28:25 +00:00
Colby Mchenry 7493fdef21 fix(scala): a chain link is never an object named like the member (#2199)
A later link of a Scala chain (`arbitrary[Int].map { ... }`) accepted any
type or object candidate: alleycats' `object map`, cats' `instances.map`,
`syntax.flatMap` and `show` objects took 418 such calls. A type or object is
now a chain link only when the receiver names what holds it - its outer type,
or for a top-level one its file's package (Scala qualified names leave the
package out): `sttp.client4.Response(...)`, `fs2.concurrent.Topic(1)`.

cats -418, sttp -5 (akka's `FormData.BodyPart`, a Java `_.stream()`), fs2
unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 20:13:54 +00:00
Colby Mchenry 71202c38df fix(python): a bare call to a locally bound name is that value (#2198)
A bare Python call whose name the enclosing function binds - an assignment
(`view = SomeView.as_view()`, tuple and annotated forms), a parameter, a
`for` / `with ... as` / `except ... as` target - or the module binds at top
level, no longer resolves to a same-named function in another file. DRF's
tests write `view = X.as_view()` then `view(request)`: 118 such calls went
to one test file's `def view`. Read from code only (docstrings and comments
blanked), and an imported name is left to the import. A pytest fixture
(`@pytest.fixture`, or anything in a conftest.py) is still what a test's
parameter of that name calls.

django-rest-framework -122, pytest -25 (`func` / `xfail` params and
aliases), allauth -12 (`serialize_instance = get_adapter().serialize_instance`),
netbox -6 (migrations' `apps.get_model(...)`), mealie -3, flask -2;
healthchecks, flaskbb, httpx unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 20:07:30 +00:00
Colby Mchenry 85aad42f6f fix(resolution): a test double is never the one-method guess (#2197)
Strategy 3's scoring already skipped a test double (`Mock…`, `Fake…`,
`Stub…`, `Dummy…`, `Spy…`) the receiver and the calling file never name
(#2175), but the unique-method shortcut before it did not: django-allauth's
`resp.json()` on a Django test-client response went to `MockedResponse.json`
193 times, as the project's one `json`. The shortcut now applies the same
rule.

django-allauth -193, pytest -15 (`MockTiming`), mealie -8 (`FakeSession`,
`_FakePostClient`); netbox, healthchecks, typeorm, eShop, BookStack
unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 19:58:52 +00:00
Colby Mchenry da1312e83b fix(kotlin): read Kotlin imports; an outside import owns its name (#2196)
The JVM import reader required a `;`, so a Kotlin file had no import
mappings at all. It now reads `import a.b.C` and `import a.b.C as D`.

- The outside-import rule from #2195 applies to Kotlin: `import
  java.net.Proxy`, `import org.mockito.Mockito.mock`, `import okio.buffer`
  own their names. A declaration the file itself makes comes first, in its
  own namespace (a callable for a call, a type otherwise) - Exposed's
  `toLocalDateTime(value)` beside `import kotlinx.datetime.toLocalDateTime`
  is the column type's member; gson's `new URI(...)` stays `java.net.URI`
  beside `TypeAdapters`' field `URI`.
- The mappings pick among same-named project classes: okhttp's
  `MockResponse.Builder()` (642) and `Request.Builder()` reach their classes,
  Exposed's `TestDB`/`SchemaUtils` the module the file imports, moshi's
  `Moshi.Builder()` (102).
- A renaming import binds its alias by FQN: kotlinx.coroutines' `import
  ...internal.unsafeFlow as flow` makes its 28 `flow { }` builders
  `unsafeFlow`, not the public `flow`.

okhttp -486 +1303, Exposed -1685 +1351 (duplicate-module reroutes; losses
are kotlin.test/kotlinx/okio/clikt/Gradle functions), moshi -21 +121, okio
-128 +51, koin -22 +11, coroutines -68 +47, nowinandroid -44 (Gradle DSL,
Compose). Java repos (gson, commons-lang, retrofit, jsoup, halo)
unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 19:45:40 +00:00
Colby Mchenry 81ce7e5695 fix(java): an import from outside the project owns its name (#2195)
A single-type or static import (`import java.lang.reflect.Field;`,
`import static org.mockito.Mockito.verify;`) whose package prefix is none of
the project's JVM packages now makes that name mean the outside class: no
reference, call or instantiation resolves it to a same-named project symbol.
Imports of the project's own classes - nested ones included - resolve as
before, since their package prefix is a project package.

Wrong edges removed: gson's production code -> a test's nested
`ParameterizedTypesTest.Field` (42), jsoup's W3C converter -> jsoup's own
`Document`/`Element`/`Node` (51, it imports org.w3c.dom), retrofit's
`java.lang.reflect.Type` -> a test class `Type` (204) and `org.junit.Rule` ->
test rules, commons-lang `IOException` -> a test's nested exception (233),
halo's Mockito `verify(...)` -> a code store's `verify`, and
`new TemplateEngine()` -> an application.yaml key.

gson -60, commons-lang -389, jsoup -51, retrofit -466, halo -83, mall
unchanged; no edges gained.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 19:33:00 +00:00
Colby Mchenry ad77e5fc8e fix(java,kotlin): extract enum constants' own bodies (#2194)
A Java enum constant or Kotlin enum entry with a class body -
`PLUS { int apply(...) { ... } }`, `NewBuffer { override fun pipe() ... }` - was
recorded as a bare enum_member: its body was never visited, so its methods
were missing and every call in them was dropped. jsoup's tokenizer
(`TokeniserState`, one `read` per state) and tree builder
(`HtmlTreeBuilderState`) are written this way.

The body is now visited under the member (QN `Op::PLUS::apply`), which
counts as class-like so its functions are methods and its properties fields.
Only a `class_body` child is visited, so Rust's struct-like variants (a
`body` field of field declarations) are untouched. Mirrored in the kernel's
Java and Kotlin walkers; kernel parity is byte-identical on the fixtures
(Torture.java gains a constant-with-body enum; torture.kt already had one)
and on gson/koin/okio.

jsoup +2156 edges, okio +138 -23, okhttp +106 -11, gson +97, halo +29,
commons-lang +16; koin, Exposed, mall unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 19:26:19 +00:00
Colby Mchenry 8410db42a1 fix(scala): block-local vals, package imports and package objects (#2193)
- A `val` the graph files under a class but that is written inside a block
  of its body (cats' `test("…") { val f = … }`) is that block's alone. It
  took `f(…)` calls from lambda parameters in other suites and from cats'
  core library (`FreeT::FlatMapped::f` from option.scala, Invariant.scala…).
  A `val`/`const` declared inside a function's lines is likewise local
  (outside C/C++, whose nesting is not trusted; a Lua global stays global).
- In a class body with no enclosing method, a binder (a lambda parameter, a
  `val`) counts as local only inside a block still open at the site.
- Only an import rooted at a value (`import builder._`) lets any type's
  members in; a lowercase package path (`import cats.syntax.all._`) did too.
- An imported object's supertypes are in scope, and `package object x
  extends Api` (no node in the graph) is read from source - imported, or the
  file's own package: sttp's `multipart(...)`, `asJson(...)`, `asEither(...)`.

cats -478 +63, sttp -153 +81 (sampled losses: other tests' locals, scalatest
`test`/`eventually`, libc `free`, `sttp.model.Header`). fs2, http4s, okio,
koin, Exposed, lodash, excalidraw, flask, kong, jekyll, halo unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 19:15:47 +00:00
Colby Mchenry 7c1c9a3691 fix(ts): export * as ns exports the namespace, not every member (#2192)
`export * as core from '../core/index.js'` was recorded as a plain wildcard,
so a barrel walk for `z.number()` went into core, on through core's own
`export * as regexes`, and landed on the `number` regex constant - ahead of
the `export * from './schemas.js'` that holds the schema function. zod's
`z.string()` (262 calls), `z.number()` (141), `z.email()`, `z.boolean()`...
all went to regex constants.

- A namespace re-export is now its own kind: it supplies only `ns`, and a
  member path through it (`z.coerce.number()`, `z.core.safeParse()`)
  continues in that module. A 3-segment chain is resolved when its root is an
  `import * as` binding and only through the import resolver - untyped chains
  stay unresolved as before.
- A named re-export (`export { safeParse } from '../core'`) is found for a
  namespace import's member; the walk compared against `'*'` and only reached
  those through the misread wildcard.

zod -471 +571 (every regex-constant edge now reaches the schema function;
`z.lt` -> core `_lt`, `z.coerce.string` -> coerce.ts), trpc +4 and kit +1
(namespace-import members through named re-exports). typeorm, hono,
excalidraw, element-plus, starlight unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 19:01:48 +00:00
Colby Mchenry e77ca83621 fix(js): resolve calls through CommonJS require bindings (#2189)
Four gaps left CommonJS calls on the local alias variable - or on the wrong
function:

- `require('../')` up to the project root produced an empty relative path, so
  its `/index.js` candidate was never found;
- a default import never followed `module.exports = X` /
  `exports = module.exports = X` / `module.exports = require('./lib/x')`;
- only the first declarator of `var a = require('x'), b = require('y')` was a
  binding, so `b()` - a package call - stayed on the local variable;
- `require('./utils').member` was read as the module's DEFAULT export: every
  such alias in express resolved to utils' first exported function
  (`normalizeType`). It is now the named member, and `exports.name =
  function impl(...)`, `module.exports.name = impl` and `module.exports = { a,
  b: c }` bind names the export index could not see.

express -1087 +727 (707 `express()` calls reach createApplication; supertest /
node:assert calls no longer edge to the local variable), eslint -750 +827
(`new RuleTester()` reaches the class, not another test's variable), mocha
-83 +90 (`run(...)` no longer lands in another spec file), koa 65 wrong
`request` edges -> `response`, body-parser +20. zod, trpc, hono, typeorm,
axios unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 18:49:15 +00:00
Colby Mchenry 60f1db03c8 fix(lua): follow a local alias to the function it names (#2188)
Kong (and OpenResty code generally) localizes everything it calls:
`local splitn = require("kong.tools.string").splitn`, `local check_phase =
phase_checker.check`, `local fmt = string.format`, `local type = type`. Every
bare call then resolved to the same-file alias variable - 8,000 edges on kong,
more than its calls into functions - a dead end for the real callee.

A bare Lua call whose name is a `local` alias in scope is now followed:
- `require "m"` (or a require-named loader like kong's `reload_module`) plus
  members, or a member of such a local, goes to the module's function of that
  name, or to what the module exports under it (`return { check = check_phase }`,
  `unindent = misc.unindent`, up to three hops);
- an alias of a Lua global or standard-library table is no edge, unless the
  project patches that function itself outside its tests (kong's `ngx.sleep`);
- a module outside the repo is no edge;
- anything else resolves as before.
A `local` reached this way is visible across files.

kong: -7057 +1150 (every loss is a std/outside alias or a reroute), indexing
4.3s -> 3.3s. apisix -3507 +170, lua-resty-core -967 +231, telescope -28 +17
(all reroutes or outside modules), lazy.nvim reroute only, roact unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 18:34:52 +00:00
Colby Mchenry 1f64e15886 fix(react-router): read links through a route-config object's getHref (#2187)
bulletproof-react writes every link as `<Link to={paths.app.discussion.getHref(id)}>`
and every redirect as `navigate(paths.auth.login.getHref(...))`, so none of its
navigation reached a route. The href reader now follows the config object to
where it is declared (through imports and tsconfig aliases), takes the string or
template the helper returns, keeps a whole-segment `${id}` as a parameter and
drops a hole glued onto a segment (a `?redirectTo=` suffix).

The Link tag pattern also stopped at the `>` of an arrow attribute written
before `to` (`onMouseEnter={() => prefetch(id)}`), which hid those links.

bulletproof-react: 0 -> 11 navigates edges; jira_clone, ecommerce-react,
react-redux-realworld, create-t3-turbo and excalidraw unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 18:17:06 +00:00
Colby Mchenry 5c3aa92f23 fix(cpp): keep classes written around macros and mid-declaration conditionals (#2186)
nlohmann/json's basic_json was lost whole. Offset-preserving preParse
fixes (the kernel parses the same text):

- an access specifier spelled as a macro (JSON_PRIVATE_UNLESS_TESTED:,
  Q_SIGNALS:) becomes the keyword it stands for, padded
- a lone attribute macro is blanked after a comment line (/// @sa
  https://…/meta/ ends in '/') and after a template head (ends in '>'),
  which read as operator continuations; a line's continuation is judged
  on its code, so 'CURLE_X, /* 100 */' still continues (curl's enums)
- a preprocessor conditional where the code before it hasn't ended a
  statement/declaration (in an initializer list, between a template head
  and its function, in an argument list) keeps its first branch:
  directives and later branches blanked; #define continuation lines and
  statement macros without ';' (RAPIDJSON_DIAG_OFF(x)) are not code

Parse-error files: redis 192->186, curl 210->203, pybind11 98->95,
spdlog 111->110, leveldb 21->20, none newly broken across json, rapidjson,
fmt, jq; kernel parity 0 diffs; basic_json 0 -> 393 members.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 18:07:46 +00:00
Colby Mchenry 2646b93829 test(ui-server): hold the engine's busiest symbol to the row cap, not 500 (#2183)
The local-only check against this repo's own index asserted LRUCache.get
has 500+ callers; sharper resolution (calls on maps and caches that were
never LRUCache's no longer link to it) brought it to 496 and the test
failed on every machine with a local index. What it checks is that the
hub's list is capped and grouped, so require more callers than the
300-row cap instead.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 17:52:25 +00:00
Colby Mchenry fcea0222f9 fix(kotlin): rewrite syntax newer than the grammar before parsing (#2182)
tree-sitter-kotlin 0.3.8 predates `when` guards (2.1), `..<` (1.9),
multi-dollar strings (2.1) and nullable function-type receivers; each
made error recovery drop the class around it (Exposed's dialect and
query classes came out as loose functions).

Offset-preserving preParse rewrites (so the kernel parses the same text):
`is X if cond ->` -> `is X,   cond ->` (the condition stops short of
`->`, so `is X -> if (…)` bodies are untouched), `..<` -> `.. `, the
`94189` prefix blanked, `T?.() ->` -> `T.( ) ->`. Parse-error files:
Exposed 65 -> 43, kotlinx.coroutines 50 -> 46; koin/okhttp/moshi/
nowinandroid/okio unchanged, none newly broken; kernel parity 0 diffs.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 17:51:32 +00:00
Colby Mchenry 5a2b5607ee perf(resolution): read a class's member lines once for member-type inference (#2181)
Member-type inference rescanned the whole class body — method bodies
included — once per distinct receiver name, and rebuilt the calling
method's body text for the shadow check on every ref; the inherited-
class-method walk re-read heads per ref. On Exposed (huge test classes)
methodCall misses cost ~3x the pre-#2171 time.

- a class's member-level lines (brace depth <= 1, comments/strings
  dropped) are collected once and each name only regexes those
- the supertype walk and the shadow check (now over the whole calling
  function) are memoized per (class|function, name)
- declared supertypes and inherited class methods are memoized

Edges unchanged on Newtonsoft/eShop/gson/Exposed/koin/horse/DRF (±2 on
AutoMapper/commons-lang/okio from the whole-body shadow check); Exposed
index 8.5s -> 7.7s, Newtonsoft 3.6s -> 3.3s.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 17:26:44 +00:00
Colby Mchenry aa0c73c416 fix(resolution): a call through a class name reaches the class method it inherits (#2180)
Strategy 1 (a receiver naming a class) looked only in the class's own
file, so an inherited class method fell to the name-word guess: Horse's
THorse.Get('/ping', …) (THorseCore's, three class(…) heads up) went to a
route-group interface's Get 120 times.

When the class itself declares no such method, walk the supertypes its
declaration heads name (Pascal class(…), Python bases, Ruby <, PHP/TS/JS
extends, and the existing Java-family / Dart / Swift / ObjC / Scala
readers), nearest first, up to five levels.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 17:13:27 +00:00
Colby Mchenry de74c050bc fix(objc): a message to super reaches the superclass (#2179)
`[super init]` was judged like a self-send, so the sending class's own
override passed (SDDiskCache's [super init] came back to itself), and
where tree-sitter-objc lost an @implementation's range no hierarchy was
read at all, so any class's init passed (NSObject subclasses' [super
init] -> SDDiskCache::init).

- a super-send excludes the class it is written in
- a lost @implementation's class is read from the enclosing method's
  qualified name (SDWebImageDownloaderDecryptor::initWithBlock:)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 17:04:59 +00:00
Colby Mchenry 2d0d120a35 fix(resolution): Solidity bare calls stay in the inheritance chain; Erlang follows -import (#2178)
- Solidity: a receiver-less call reaches a function of the contract
  around it or one it inherits (heads read from source, `is` clause with
  constructor args dropped); OpenZeppelin's _msgSender() in Context-based
  contracts went to ERC2771Context's override 83 times.
- Erlang: another module's function is called bare only through
  -import(Mod, [f/N]) (or from an included .hrl): cowboy's
  -import(req_SUITE, [do_get/3]) went to compress_SUITE's do_get/3; with
  no import, no cross-module guess.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 16:59:19 +00:00
Colby Mchenry 48498021e6 fix(resolution): calls on a library's types and modules stay the library's (#2177)
- Rust: a CamelCase path receiver the project doesn't declare (Vec::new,
  String::from, Default::default — not Self, not a SCREAMING static) is
  an outside type; a lowercase module path (task::spawn, io::stdout)
  never reaches a method. tokio: task::spawn -> TaskTracker::spawn x300.
- Rust/Go: a chain link on a later line (bat()\n.stdout(…)) is chained,
  named after the chain's head; std::process / assert_cmd pipe and
  assertion names join the Rust std list.
- Go: w.Header().Get/Set/… is net/http's Header.
- R: a plain call never reaches an object's method (base range(x) went
  to a ggproto Coord's range) — exact and fuzzy paths.
- JS: fetch body methods (text/json/arrayBuffer/blob/formData) are
  builtins.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 16:51:48 +00:00
Colby Mchenry 5844eb8986 fix(cpp): a qualified name reaches a declaration in a macro-opened namespace (#2176)
fmt opens its namespace with FMT_BEGIN_NAMESPACE, pybind11 with
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE), rapidjson/spdlog with
…_NAMESPACE_BEGIN — the extractor sees a bare macro line, so every
declaration inside is indexed without its namespace and fmt::format(…)
(1,728 calls on fmt) resolved to nothing.

As a last resort for a qualified C/C++ name, read the project's
namespace macros (object-like and single-parameter function-like
openers, through identifier aliases, inline namespaces transparent;
closers by brace count), the ranges each opens per file, and namespace
aliases (namespace py = pybind11;), and match the declaration whose
macro prefix + qualified name is the name called. fmt +2480, pybind11
+1446, spdlog +264; tokio/serde/gin/excalidraw/Newtonsoft unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 16:38:59 +00:00
Colby Mchenry c0a38a8758 fix(resolution): a receiver-name guess is never a dispatched handler or an unnamed test double (#2175)
The receiver-word guess picked framework-dispatched handlers and test
doubles by a single shared word: django-allauth's test client.post(…)
landed on a ClientRegistrationView's post 425 times, mealie's
response.json() on a test's _FakeHTTPResponse 424 times.

- view / viewset / controller / endpoint (and view-mixin) HTTP-verb and
  CRUD-action methods are ruled out of the guess (and the ruling-out can't
  leave a lone guess behind — narrowed)
- a Fake/Mock/Stub/Dummy/Spy owner is guessed only from a file that names
  it or a receiver named like a double
- Python: `x = Model.objects.create|get|first|last|latest|earliest(…)`
  types x as Model (Django manager calls), so x.save() reaches the model

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 16:27:41 +00:00
Colby Mchenry c6491ff62d fix(kotlin): record infix calls as calls (#2174)
`Users.id eq id1`, `single { … } bind I::class`: an infix_expression was
never extracted, so a project's infix DSL had no callers (Exposed's
`eq` > 1,000). Extract it as a call of the middle simple_identifier —
`lhs.fn` when the left operand is a plain name, else bare — in the TS
walker and the kernel (kernel-parity 0 diffs on okio/okhttp/Exposed).

A standard infix name on an expression receiver is gated like a chain
link (`alias(x) apply false` in a build script); a project's bitwise
extension on a number type (okio's `Byte.and`) is never chosen, typed or
guessed, since the stdlib's Int/Long members take `x and 0xff`.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 15:52:23 +00:00
Colby Mchenry 0b74e94ee7 fix(kotlin): no automatic semicolon before a same-line infix e-word (#2173)
tree-sitter-kotlin 0.3.8's external scanner inserted an automatic
semicolon on the same line before any word starting with `e` other than
`else`, so Exposed's `Users.id eq id1` ended its statement early and
error recovery could swallow the class around it (r2dbc UpsertTests
became one loose function nesting every test).

Patch scanner.c (same line: never a semicolon before an e-word) in both
the vendored kernel C and the wasm build; provenance, shas and rebuild
recipe in docs/grammars/tree-sitter-kotlin.md. Exposed files with a parse
error 229 -> 71 of 1,004; koin, nowinandroid, moshi, okio, okhttp and
kotlinx.coroutines unchanged (one okhttp file fixed, none broken);
kernel-parity 0 diffs.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 15:36:22 +00:00
Colby Mchenry 2e2b98c94e fix(kotlin): a bare call reaches only a member in reach (#2172)
A receiver-less Kotlin call exact-matched any class's same-named method:
koin's module { } in one test went to another test class's private
module 268 times, error("…") to a Logger's error, get() inside a
definition lambda to Koin's get rather than the Scope it runs on.

A bare call now reaches a method of a class, anonymous object or extension
receiver around it (read from nodes and from the source's braces, so a
class the parser lost still counts) and what those inherit (with Android
framework ancestry), of a type the project's function types take as a
lambda receiver (plus outside types it writes extensions on), or of an
object the file imports. with/apply/run blocks and .kts scripts are left
unjudged; Kotlin's require/check/assert preconditions are the stdlib's.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 15:20:22 +00:00
Colby Mchenry f9530d7d5f fix(resolution): a call through a declared member calls that member's type (#2171)
C# / Java / Kotlin receivers that are fields, properties or parameters were
typed only when declared in the calling method without generics, so a call
through a field fell to the receiver-word guess: Newtonsoft's
_innerWriter.WriteValue() landed on the wrapper's own WriteValue, a
TextWriter's Write() on a test writer's, commons-lang's DateFormat field's
parse() on DateParser 284 times.

- read class-level member declarations (brace depth 1, comments skipped),
  walking base classes with their type arguments substituted, C# using
  aliases, type-parameter bounds, generic and for-each declarations
- C# interface members default public and namespace-level types internal
  (TS extractor + kernel, parity 0 diffs); interface calls resolve at all
- the receiver-word guess weighs the owner type's name only, the receiver's
  last link (a constant's type), and its head noun; .NET std names need a
  receiver named after the owner; production code never lands in a test
  suite's mock

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 14:54:31 +00:00
Colby Mchenry a39fd14e89 fix(swift): read a call's shape before reaching a member by name (#2170)
Swift's extractor keeps one receiver level, so super.init(…), a longer
chain's removeAll() and a bare min(a, b) all arrive as bare names and
exact-matched whichever project member shared the name. A bare or self./
super. call now reaches only a member of the types around it and what they
inherit or conform to (source-read heads, UIKit ancestry, the standard
collection protocols), nearest first; a standard-named chain link needs a
receiver that names the owner, an outside owner, or an argument label only
the project's method takes; playground and test-target globals stay put.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 13:43:36 +00:00
Colby Mchenry 791ae397e5 fix(resolution): a receiver declared with an outside type calls that type (#2169)
When a receiver's type is inferred from its declaration or initializer and
the method isn't on it, the matcher used to fall through to its
capitalized-name and name-overlap guesses. If the inferred name is a type
(capitalized last segment) that nothing in the project declares — JDK /
BCL / okhttp / Django types — it now stops: the method is that outside
type's. Python's initializer pattern no longer reads a keyword argument
(`prefix=IPNetwork(…),` inside a call) as a binding of `prefix`.

A/B (edges removed): commons-lang -1296 (`append` / `length` / `charAt` on
JDK types), Newtonsoft.Json -496 (`MemoryStream.ToArray`, `ToString`),
gson -367 (`List.add`, `Map.put`), retrofit -321 (okhttp `Request.headers()`
/ `method()`), jsoup -130, okio -26, pytest -44 (pathlib / io), netbox -25,
healthchecks -9 (`TimestampSigner.sign`), flask -9, zod -1.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 13:18:33 +00:00
Colby Mchenry b7b2a44cfa fix(kotlin): split primary constructors keep their class; std chain names stay std (#2168)
- tree-sitter-kotlin can't read `class Name` / (comments) / `internal
  constructor(…) : Super {` on separate lines — the class is dropped and its
  members become loose top-level functions (okio's ByteString, Kotlin
  Multiplatform's `expect` classes). The Kotlin preParse now blanks that
  constructor's modifiers / annotations / keyword to spaces, so `(…) :
  Super {` parses as the primary constructor; offsets and lines survive, and
  preParse runs before the kernel, so both paths parse the same bytes.
- With members back on their classes, a Kotlin standard-library name on an
  untyped chain link (scope functions, collection / string / conversion
  methods — not names project types commonly carry) needs a receiver named
  after its owner, in the exact / fuzzy filters and the unique-name guess.

A/B (edges removed / added): okio -1028/+1127, okhttp -979/+1145,
kotlinx-datetime -426/+515 (mostly re-keyed onto recovered classes),
koin -60/+4 (`msg.contains(…)` → a dependency map's `contains`).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 13:09:06 +00:00
Colby Mchenry 715c62b0d9 fix(rust,go): read a call's shape before guessing its method (#2167)
The extractors keep one receiver level, so chain links arrive as bare names.
Read at the call site:
- bare (no `.` / `::` before it): never a method — Rust reaches one only
  through `self.` / `Type::`, Go through a receiver (axum's routing
  `get(handler)` went to a cookie jar's `get`, tokio's `drop(x)` to `Drop`
  impls, #1861's pinned `reset()` → `Target::reset` is now the free fn);
- chained (after `.`, receiver read back through arguments, macros `name!`,
  `::` paths and Go composite literals): never a free function, and a
  standard-library method name (Option/Result/iterator/collection/string
  methods; Go's String/Error/Header/locks/reflect/time) needs a receiver
  named after its owner — project-specific names keep their match (clap's
  `flag("n").short('n')` → Arg::short, cobra's `c.Root().Name()`, gin's
  `c.Set(…)`).
The same std gate applies to the method-call matcher's unique-name guess.

A/B (edges removed / added): clap -1496/+93, tokio -1064/+235, axum
-695/+42, ripgrep -477/+32, cobra -167, serde -119/+1, bat -40/+4, gin
-32/+26.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 12:58:08 +00:00
Colby Mchenry 05740b55eb fix(resolution): a call on an outside type is not a project method (#2166)
`Name.method()` where `Name` reads as a type in the language's conventions
and nothing in the project (same language family) is called `Name` — JDK /
BCL / DOM / Indy types — now leaves the method-call matcher before the
capitalized-receiver and name-overlap guesses. Not applied in Go (exported
package variables), C / C++ / Rust / CUDA / Metal; in Pascal only Delphi
type prefixes (T/E/I…) and `Exception` count, since parameters and locals
are capitalized there too.

A/B (edges removed): commons-lang -1636 (`Integer.valueOf` → StringUtils
×804, `Calendar.getInstance`, `Instant.now`), horse -89 (`Exception.Create`
→ EHorseException), retrofit -79 (OkHttp `RequestBody.create` / `HttpUrl.get`),
typeorm -74 (`Object.assign` → ObjectUtils), gson -100, zod -63,
Newtonsoft.Json -57, jsoup -18; gin and cobra byte-identical.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 12:44:03 +00:00
Colby Mchenry 37bcd5c0ae fix(react-router): nested, lazy and constant routes; per-app tsconfig aliases (#2165)
- Route tables compose nested paths: a child's `path` is relative to the
  path-bearing routes around it (`children` of a data router, nested
  `<Route>` elements), absolute ones reset. `lazy: () => import('./x')`
  (object and JSX) renders the module's default export (`Component` export
  as fallback) via a claimed `lazy-import:` ref. `path: paths.app.root.path`
  keeps its parts on the node's signature and is named in postExtract from
  the constant's object literal (found through the file's import). The
  route keeps its extracted id; `isReactRouterRoute` accepts it. An element
  wrapped in parentheses or a guard (`<ProtectedRoute>`, `<Suspense>`,
  `*Provider`, `*Guard`) renders the first element inside.
- Import aliases: the tsconfig / jsconfig nearest the importing file that
  declares `paths` is tried before the root's, so each monorepo app resolves
  its own `@/*` / `~/*`.

A/B (edges removed / added): bulletproof-react -43/+367 (0 → 9 named,
linked routes; cross-app imports fixed), react-native-reusables -103/+193,
kit -184/+218, trpc -45/+71 (examples' `~/…` now their own), create-t3-turbo
-1/+13; excalidraw, halo, obytes, realworld byte-identical. #1348's nested
fixtures now expect composed paths (`/dashboard/settings`, `/data/prefs`).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 12:34:11 +00:00
Colby Mchenry cf449fdad7 fix(resolution): a name destructured from a composable reaches what it returns (#2163)
`const { getDefaultActivityRoute } = useDefaultActivity(); getDefaultActivityRoute()`
bound the name locally, which ruled out every cross-file candidate, so the
call resolved to nothing. A bare JS-family call through a name destructured
(`{ key: alias }` included) from a call's result now resolves the callee —
through the file's imports, else the same file, else the project's one
function of that name (Nuxt's `~/` alias) — checks its source returns the
key, and takes the function declared in its body, else the module-level one
it returns. A later declaration at the call's scope shadows the binding.
Returned object literals now count as exporting their names for #2162's
unexported-binding rule.

A/B edges added (removed): mealie +176 (-4), excalidraw +156 (`const { t } =
useI18n()`), halo +58, kit +34 (-14, `validate` now the validator's own),
elk +19; trpc byte-identical.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 12:15:50 +00:00
Colby Mchenry d03e4dbf6f fix(resolution): an unexported ES module binding is not reachable cross-file (#2162)
The sealed-module rule only covered modules exporting nothing. Per symbol
now: a JS/TS top-level binding declared by its own statement (function,
const/let/var, class, interface, type, enum) in an ES module is reachable
from another file only if it's exported — the `export` keyword (node flag or
its line), an `export { a as b }` list, `export default x` or
`export default { a, b }`. Classic scripts, CommonJS, `declare global`,
`.d.ts`, qualified members, object-literal members (zustand store actions)
and `proto.x = function x()` are exempt. Applied at the reachability gate,
so the winner is rejected rather than another promoted.

A/B (edges removed / added): zod -229 (package `z` imports → a local `z`),
excalidraw -66 (DOM `Element` → a test helper's type), kit -50, typeorm -47
(`import { ColumnMetadata } from "typeorm"` → a fixture's local), trpc -26,
hono -8, axios -1; express byte-identical.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 12:07:48 +00:00
Colby Mchenry 2d1b644133 fix(extraction): Flow-typed JavaScript is read as TSX (#2161)
tree-sitter-javascript can't parse Flow annotations; `render(): React.Node`
ended a class early (segmented-control's component had no methods). A
`.js` / `.jsx` whose leading comments carry `@flow` (not `@noflow`) now
detects as `tsx`, and the TS extractor's offset-preserving preParse blanks
Flow-only syntax for those files: exact object `{| |}`, a maybe type's `?`,
an inexact object's bare `...`, `import typeof`, `opaque type`. preParse is
hoisted before the kernel, so both paths parse the same bytes;
kernel-parity now routes .js/.jsx through detectLanguage too (RN core
Libraries: 0 diffs over 641 files). Grammar preload adds tsx whenever
JavaScript is present.

Parse errors over 400 RN-core Flow files: 10,626 (JS grammar) → 391 (TSX).
A/B edges: RN core Libraries 29,649 → 38,194 (+12,639 / −4,094, the
removals mostly `root.render(…)` → the one DrawerLayoutAndroid::render the
old parse left, and edges re-keyed onto recovered methods);
segmented-control +35/−5; express and react-native-netinfo byte-identical.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 11:55:43 +00:00
Colby Mchenry 7aa124c0aa fix(scala): a bare call reached by name alone must be in reach (#2160)
Read at the site:
- a later link of a chain (the extractor keeps one receiver level; the line
  shows the dot) reaches a member whose owner the receiver chain is named
  after (call / type arguments dropped: `proc("x").call()` → proc,
  `checker.value.onWrite` → checker), or a same-file member; never a package
  object's function unless it is a Scala 3 `extension` method;
- a name the enclosing def binds (parameter, val/var/def, lambda / for param)
  is only a def inside that function;
- otherwise a member of the types around it, an anonymous subclass's bases
  (`new OptionParser[C]("x") { head(…) }`), their extends/with supertypes
  (source-read heads), same file, or an imported object (`import Foo._`,
  `import Foo.{bar}`); a value import (`import builder._`) leaves the file
  unjudged.

A/B (edges removed / added): cats -1693/+678, sttp -887/+271, munit
-265/+175, scopt -245/+78, os-lib -115/+43.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 11:46:04 +00:00