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>
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>
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>
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>
- 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>
`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>
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>
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>
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>
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>
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>
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>
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>
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>
`[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>
- 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>
- 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>
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>
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>
`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>
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>
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>
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>
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>
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>
- 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>
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>
`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>
- 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>
`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>
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>
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>
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>
The README said every bridge hop carries `metadata.synthesizedBy`, but only
the synthesized channels (rn-event-channel, fabric-native-impl) did; a
resolver-made hop (Swift↔ObjC, RN legacy / TurboModules, Expo Modules JS)
carried only `resolvedBy: 'framework'`, and `expo-module-extract` named
nothing that exists. The framework loop now adds `metadata.framework` (the
resolver's name: swift-objc-bridge, react-native-bridge, expo-modules-js,
fabric-view, rails, laravel, …) and the README says what each kind carries.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- FastEndpoints: an endpoint class (`: Endpoint<…>`, `EndpointWithoutRequest`,
`Ep.…`) declares `Get(…)` / `Post(…)` in `Configure()`; each becomes a route
linked to the class's own HandleAsync / ExecuteAsync (the bare-member scope
from #2151 keeps two `List` endpoints apart). A constant path
(`CreateContributorRequest.Route`) is read in postExtract from wherever the
class declares it; `$"/{nameof(Project)}s"` reads as `/Projects`.
- `app.MapGet("api/todos", …)` is named `/api/todos`.
A/B: ardalis/CleanArchitecture 0 → 24 routes (+24 edges); eShopOnWeb and
eShop only re-key routes whose path gained a leading slash; realworld and
jasontaylordev/CleanArchitecture byte-identical.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Detection only looked at the root (`build.sbt`, `conf/routes`,
`conf/application.conf`); playframework/play-samples keeps each project's
`conf/routes` in its own directory and had no routes. Any indexed Play routes
file now counts. play-samples 0 → 139 routes (+108 edges); root-level Play
repos are unaffected.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- Route files (`routes.draw`, or under config/routes/) are read as a block
structure: `namespace` / `scope` add path + controller module, nested
`resources … do` sit under `/:parent_id`, `member` / `collection` add
`/:id/x` / `/x` actions, `root` and `resource` / `resources` options
(`only:` / `except:` across lines, `path:`, `controller:`) are honored;
`do` and `if` / `unless` / `case` / `begin` open frames an `end` closes.
- Detection accepts `gem "rails"` (double quotes), railties, and a Rails
engine's `config/routes.rb` anywhere (solidus).
- A namespaced `controller#action` only reaches controllers of its own
module path when any exist — an admin controller that inherits `index` is
not the API's `ZonesController#index`.
Route→action edges main → this branch: mastodon 221→578, solidus 0→319,
rubygems.org 108→192, maybe 127→189, lobsters 141→196; no non-route edge
lost except solidus's CamelCase refs now resolved by the Rails model
resolver (`Order` → Spree::Order model, was a state-machine module).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
`bp.add_url_rule('/path', 'name', view_func | positional view, methods=[…])`
and a project's own wrapper that hands a list of paths and a `view_func=`
over (flaskbb's `register_view(auth, routes=[…], view_func=X.as_view(…))`)
now produce route nodes linked to the view. A class-based view is ANY unless
`methods=` says; a function defaults to GET. A call whose path isn't a
string literal (the wrapper's own `add_url_rule(route, …)`) adds nothing.
flaskbb 0 → 103 routes (+104 edges); flask's own tests +27; CTFd, microblog,
bluelog byte-identical.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The unique-name method guess no longer applies to a Lua/Luau call through a
standard or host library table (string, table, math, io, os, coroutine,
debug, utf8, package, bit, jit, ffi, vim, ngx, busted's assert/spy/stub/mock,
love) unless the project defines that function on the table itself (kong's
globalpatches define ngx.sleep), nor to a string method called on a value
(`s:find`, `s:match`, `s:gsub` …). Other untyped receivers keep the guess:
a word rule like Ruby's was tried and lost koreader's OO calls
(`self.ui:reloadDocument()` → ReaderUI).
A/B (edges removed / added): kong -2742, koreader -101, lazy.nvim -84,
telescope.nvim -47, Fusion -9, lua-resty-core -1.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
PHP joins the untyped-receiver languages for the unique-name method guess:
`$x->m()` takes the one project method named `m` only when the receiver
shares a word with its owner, or names a class (the whole name, or its last
camel word: `$newRole` → Role) whose ancestry — extends + trait `use`,
namespace-aware — has the method.
A/B (edges removed / added): guzzle -359, BookStack -302, console -124,
Slim -124, monica -53. Removals are PSR-7 / framework / exception calls
bound to a test double or unrelated helper; the deliberate loss is calls
through receivers named after neither (`$loaner->loansAsLoaner()`).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A bare C# name (a `calls` or `references` ref with no dot, where the line
shows no receiver) can mean a member only of the types around it (outer
classes included), their base types (read from `class X : Base, IFoo`
heads, every partial declaration), or a static using: the file's
`using static`, any `global using static`, or `<Using Include="…"
Static="true"/>` in the .csproj / Directory.Build.props files above it.
Names outside any recovered type are not judged.
A/B (edges removed / added): Newtonsoft.Json -1394/+910 (enum-valued
properties now reach the enum type), AutoMapper -135/+58, eShop -117/+5,
serilog -20/+17, CleanArchitecture 0.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(objc): a message reached by name alone must be in reach
- `[self m]` / `[super m]` / `[[self class] m]` (receiver dropped by the
extractor) reach only methods of the sender's hierarchy, read from
`@interface Name : Super` plus a small UIKit/AppKit/MapKit superclass table
for categories on system classes. When tree-sitter-objc loses an
@implementation and its methods become functions, a same-file function,
one in the file named after a hierarchy class, or any when the sender's
class itself was lost, still counts; `[super init]` never lands on another
file's function.
- C call syntax (`completionBlock()`) never means a method or property.
- Local receivers are typed from ObjC declarations (`FMResultSet *rs`,
`(nullable FMResultSet *)rs`, block params), and objc joins the untyped-
receiver languages for the unique-name guess (`weakSelf` / `strongSelf`
count as self). Shared receiver words must be 3+ letters so class prefixes
(`SD`, `NS`) don't match; Ruby/CFML unchanged but one wrong lobsters edge.
A/B (edges removed / added): SDWebImage -321/+47, fmdb -28/+85,
CocoaLumberjack -19/+10, AFNetworking -13/+1, MBProgressHUD 0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(objc): a class or typed-property receiver keeps its inherited method
`[AllTypesObject objectsInRealm:…]` (a class method inherited from RLMObject)
and `[managed.anyDataObj containsObject:]` (a property declared
`RLMSet<…> *anyDataObj`) now keep the unique-name match when the receiver's
class — or one of the property's declared types — has the method's owner in
its hierarchy. realm-swift goes from -2562/+588 to -504/+529 vs main.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
VB.NET's extractor keeps a call's last name only, so `Me.Panel.Controls.Add(x)`
and `New System.Drawing.Size(1, 2)` arrived as bare `Add` / `Size` and exact /
fuzzy matching took any class's member (even an enum case). The receiver is
now read from the line: a member candidate needs `Me` / `MyBase` / `MyClass`,
its own type or module as the last receiver segment, or a `(Of T)` type
argument (`GetService(Of Notifier).Notify()`). Genuinely bare names are
unchanged.
A/B (edges removed / added): SCrawler -3452/+6, remoteapptool -143/+0,
CompactGUI -31/+5, ViVeTool-GUI -1, TaskbarX 0.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- A bare call in a .cfc reaches only the component's own methods and those
of the components it extends (extends="a.b.C" read from source, matched
to indexed .cfc files by longest path suffix). Calls in .cfm templates and
multi-line chain links (name not on the ref's line) are not judged.
- The untyped-receiver rule from the Ruby fix now covers CFML too: the one
project method named `m` needs a receiver named after its owner. The word
match also accepts the receiver's whole name (`cbsecurity` → CBSecurity)
and ignores trailing digits (`executor1` → Executor); Ruby is
byte-identical to main.
A/B (edges removed / added): coldbox -2429/+295, TestBox -255/+31,
fw1 -103/+4, cbsecurity -8/+5, cfdocs 0. Deliberate losses: framework-repo
calls through conventionally named, untyped receivers (ColdBox's `event.*`
→ RequestContext, TestBox's `variables.assert.*` → Assertion).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Ruby types nothing, so two guesses produced most of a Ruby repo's wrong
call edges:
- `receiver.m()` through an untyped receiver took the project's one method
named `m`. It now also needs a word shared between the receiver and the
method's owner (`web_push_request.legacy_encrypt` → WebPushRequest);
`self` / `self.class` are exempt.
- a bare `m()` inside a class took any class's `m`. It now reaches only the
class's ancestry — superclasses and include/extend/prepend modules, read
from source and resolved against the lexical nesting. Calls in module
bodies and top-level blocks (specs, DSLs) are not judged.
A/B (edges removed / added): rubocop -4406/+262, mastodon -2642/+540,
jekyll -343/+10, lobsters -243/+4, sinatra -207/+2, devise -112/+3.
Deliberate losses: instance_eval'd DSL blocks inside a class method
(sinatra's own tests' `mock_app { get '/' }`), and helpers on a framework
class the repo reopens past an external superclass (devise setup_mailer).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A Kotlin call's top-level candidate (a function, an extension function, a
property) must be in the caller's package, imported by name, or in a
star-imported package. Class members aren't judged: a lambda's receiver can
put any type's members in scope.
A function type with a qualified receiver (`(A.Builder.() -> Unit)?`) broke
the tree-sitter-kotlin parse badly enough to drop the enclosing class;
an offset-preserving preParse blanks the qualifier (hoisted, so the kernel
parses the same bytes — parity 0 diffs on Exposed, okhttp, nowinandroid).
A/B (edges removed / added): Exposed -5370/+4256 (1,318 R2DBC assertEquals
misroutes; DatabaseTestsBase's members and 250 extends/imports edges back),
koin -965/+960, okhttp -690/+675 (567 MockResponse to the imported
mockwebserver3 class), okio -98/+44, nowinandroid -14.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A receiver-less Dart call now reaches only a method of the enclosing class,
something it extends / mixes in / implements, or an extension on one of those
types, and the nearest one wins. The hierarchy is read from each declaration's
head (comments and type arguments dropped), since supertype edges don't exist
on the first pass. Body-less abstract members (extracted as owned functions)
are scoped too.
Mixin-application classes (`class A = B with C;`) were extracted as
`<anonymous>` with no supertypes; both the wasm extractor and the Rust kernel
now name them and emit their extends/implements refs (kernel parity verified
on bloc, riverpod, shelf, dio, getx).
A/B (edges removed / added): riverpod -1945/+517, shelf -785, getx -855/+18,
bloc -178/+95, dio -78.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
testthat runs each test file in an environment of its own, so a variable a
test assigns is invisible to the package and to other tests. ggplot2's
`c <- ggplot(…)` in test-facet-grid-.R was the one node named `c`, and took
2,455 of the package's `c(…)` calls (the sweep's top-depended symbol).
isVisibleAcrossFiles rejects an R variable/constant under `tests/` from
another file — except `helper-*.R` / `setup-*.R`, which testthat sources for
every test. (A first cut that limited every R call to function nodes lost
ggplot2's factory-defined `geom_point <- make_constructor(…)`, dplyr's
`summarize <- summarise` and shiny's aliases.)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A Lua `local` (variable or function) belongs to the file that declares it,
but cross-file name matching treated it like a global. kong's spec helpers
re-bind busted's globals (`local it = it`, `local assert = require
"luassert"`), and every other spec file's `it(…)` / `assert(…)` /
`describe(…)` linked to them — 15,776 edges; koreader's `local ipairs =
ipairs` in one module took the project's `ipairs` / `pairs` / `type` /
`tonumber` (2,329).
isVisibleAcrossFiles now reads a Lua/Luau variable or function's
declaration line and rejects a `local` one from another file (the reference
stays unresolved rather than taking a runner-up). Globals stay visible.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>