Commit Graph
1574 Commits
Author SHA1 Message Date
Colby MchenryandClaude Opus 5.5 accf600e47 fix(ts): a declaration in a declare module block is reached through its module (#2728)
A declaration file's declare module 'x' { ... } declares the members of
x, which no other file names by a bare name: an import of x reaches them.
The functions of such a block have been held to that since #2718, but its
other declarations, the types, classes, constants and enums, still took
bare-name matches from any file. On vscode, Node's
import { readFileSync } from 'fs' linked to the fs that vscode.d.ts's
workspace namespace declares, electron's clipboard and shell to env's, a
ProcessEnv to the proposed API's, and an LSP TextDocument re-exported
through a project barrel to the API's own.

Every block-level declaration now takes a match from another file only
when the reference is written in a block declaring the same module (the
proposed API files merge into vscode.d.ts), is a type position in a file
that imports the module (vscode.TextDocument under a namespace import,
which the extractor records by its last name), or names what the file
imports from a project file that imports the module and re-exports it
(copilot's vscodeTypes.ts).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 05:01:49 +00:00
Colby MchenryandClaude Opus 5.5 f66d064279 fix(cpp): a function is named by its declarator, never by its noexcept, throw or requires clause (#2727)
Both C++ extractors name a function by the first qualified identifier
found breadth-first in its declarator, which is how an out-of-line
`Foo::bar` is named. The walk skipped only the parameter list and the
trailing return type, so a qualified name in the clauses after them
became the name of a function whose own name is plain. nlohmann/json's
constructor `basic_json(CompatibleType&& val) noexcept(noexcept(
JSONSerializer<U>::to_json(…)))` was indexed as a method
`JSONSerializer::to_json` that doesn't exist, with no node for the
constructor; `throw(std::bad_alloc)` named a method `std::bad_alloc`,
and a trailing `requires std::integral<T>` a function `std::integral<T>`.

The walk now also skips `noexcept`, `throw_specifier` and
`requires_clause`, in the TS extractor and the kernel alike; the parity
fixture covers all three.

Measured on main 226ac930 with fresh indexes, and byte-identical again
after syncing onto e97012eb: json 42, abseil 28 and
protobuf 1 phantom nodes become the real functions at the same lines;
fmt, leveldb, rocksdb, redis and three controls in other languages are
byte-identical. Kernel parity sweeps on json, fmt, abseil, leveldb and
rocksdb find no file with diffs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 04:20:15 +00:00
Colby MchenryandClaude Opus 5.5 e97012eb31 fix(ts): an interface links the interfaces it extends (#2726)
tree-sitter-typescript holds an interface's supertypes in an
extends_type_clause, which neither extractInheritance (wasm) nor the
kernel's extract_inheritance read: a class's extends and implements
became edges, an interface's extends nothing. So an interface had no
place in the type hierarchy, a change to it reached none of the
interfaces built on it, and a call through a composite interface whose
head the receiver typing could not read fell to name matching
(home-assistant/frontend's hass.formatEntityName linked to a namesake
in an unrelated file).

Both engines now emit one extends reference per type of the clause: a
name, a qualified name as written (shapes.Shape), or a generic type by
its name field (Repo for Repo<User>). They resolve as a class's do, so
a package's type stays unresolved, and a namespace import or a module
the repository declares resolves.

A supertype a TS/JS module names without importing it is now its own
declaration or a global one, at a script's top level or in declare
global, never another module's or a namespace's: vscode's interfaces
extending Omit<...> linked to quickInput.ts's export type Omit, and
extends Function to terminal-suggest's Fig.Function.

A call on a TS/JS type whose declaration is known now takes the method
from that declaration's own supertypes. The walk by name, which unions
the supertypes of every same-named type and looks the method up by name
again, follows only classes' supertypes, as before interfaces had any.
Otherwise vscode's receiver.dispose() on an ILocalOtlpHttpReceiver
extends IDisposable reached copilot's own IDisposable, one of eleven,
and stream.on() on a NodeJS.ReadableStream the project's
ReadableStreamEvents.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 03:44:24 +00:00
Colby MchenryandClaude Opus 5.5 226ac930de fix(cpp): a call through a written class template specialization, like formatter<int>::format(…), never reaches another specialization's member (#2725)
The index names a class template specialization's out-of-line member
after the template: fmt's test-only `auto
fmt::formatter<incomplete_type>::format(…)` is `fmt::formatter::format`.
So `formatter<int>::format(42, ctx)` in fmt's tests reached that member,
defined in another file, and `formatter<std::tm, Char>::parse(ctx)`,
where the template declares no such member, fell through to a guess by
the receiver's name: `native_formatter::parse`.

- A C++ call whose qualifier is written with template arguments keeps a
  candidate defined out of line for a full specialization of its class
  only when that specialization's arguments are the call's. A template's
  own members, which have a `template <…>` header with parameters,
  partial specializations' members and members defined in their class
  stay. This applies beside the existing specialization pool in the exact
  and partial qualified-name matches and in cppMethodOf's own members.
- When the class strategy finds no member for a call through a project
  class template's written specialization, matchMethodCall stops: a
  method picked by the receiver's name would be another class's.

Measured on main f6d8f19b: fmt 17 wrong edges removed; json's 16
changed sites all point at a phantom node (a noexcept clause read as a
declaration, filed separately); rocksdb, protobuf, leveldb, abseil,
redis and seven controls in other languages are byte-identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 03:10:30 +00:00
Colby MchenryandClaude Opus 5.5 f6d8f19b29 fix(rust): a call through a trait-bounded generic, like Key::get(…) with Key: MapKey, reaches the bound trait's method (#2724)
A Rust call through a generic type parameter is a call of the method its
bounds bring in: protobuf's `Key::get(…)` in `fn map_get<Key: MapKey>`,
with `trait MapKey: … + FfiMapKey`, is `FfiMapKey::get`. No strategy
read the bounds for such a path. These calls linked only when a
qualified name happened to end with the written text, which the scope
boundary of the previous fix rightly stopped, and a parameter bounded
only by an outside trait (`T::default()` under `T: Default`) could reach
a project type's unrelated `default` by name.

matchRustGenericPath, right after the `Self::x` branch of
matchMethodCall, reads the bounds the function and the impl or trait
around the call put on the parameter (in `<…>` or a `where` clause) and
resolves the method among those traits and the traits they extend,
through the helpers typed receivers already use. Outside-only bounds,
markers, and two traits declaring the method give no edge. The ref is
recorded as typed, so a trait method's missing `pub` doesn't hide it
from another module.

Measured on main ac9f96f8: protobuf +47, tokio +43, axum +14, clap +10,
serde +7, nothing removed; ripgrep and eight controls in other languages
are byte-identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 02:25:32 +00:00
Colby MchenryandClaude Opus 5.5 ac9f96f8a3 fix(cpp,rust): a qualified call like Status::OK() links the class it names, not one whose name ends the same way (#2723)
The partial qualified-name match took any declaration whose qualified
name ended with the written text, with no scope boundary. In
facebook/rocksdb about 4,100 calls to `Status::OK()`,
`Status::InvalidArgument(…)` and their siblings went to `IOStatus`'s
methods of those names; base-class calls from an override, like
`return FileSystemWrapper::NewWritableFile(…)` or leveldb's
`return DB::Put(…)`, went to a namesake subclass or to the calling
method itself; and ripgrep's `Path::new(…)` on std's `Path` went to its
own `PrinterPath::new`.

- The written name must start a scope of the candidate's: the
  candidate's qualified name is the name, or ends with it right after
  `::` or `.`.
- Among those candidates, the ones in the call's own language come
  first. protobuf's C++ `DescriptorPool::generated_pool()` met a Ruby
  namesake first, which the language gate then dropped.
- A C++ name qualified by a template parameter, like
  `CacheShard::ComputeHash` in `template <class CacheShard> class
  ShardedCache`, gets no edge from any strategy, except to a member of
  the parameter's default (`typename Parser = TcParser`). The boundary
  alone moved rocksdb's call to the receiver-word guess, with the same
  wrong target.
- The C++ template-head reader names each parameter by its last word,
  `int N`, `detail::fixed_string S`, `const PrefetchOpts& kOpts`, and
  keeps its default. A `<…>` after a C++ declaration's name is a
  specialization's arguments, not parameters. Before, `WireFormatLite::
  FieldType kKeyFieldType` made `WireFormatLite` a type parameter.

Measured on main bd62d9c0 and re-checked on 17f1453a: rocksdb 4,219
retargeted, 83 added, 30 removed; protobuf 24, 47, 40; fmt 7, 0, 21;
leveldb 2, 0, 0; ripgrep 0, 0, 38. Nine Ruby, Java, C#, C, JS, Python
and Go controls are byte-identical. Only C++, Rust and Ruby reach this
match; the file-path suffix match is already anchored and is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 01:53:51 +00:00
Colby MchenryandClaude Opus 5.5 17f1453a6c fix(rust): a value's declared type is its binding's in scope at the call (#2722)
The type a Rust value is written with, read for `reader.as_mut().m()`
and calls through a `Pin`, came from a backward line scan for `reader:`
in the calling function. That took a `let reader: …` in a block the call
is outside of, or a struct literal's `reader: …` field, for the
declaration; it also stopped at a `let entry = entry.as_ref()…` on the
call's own line, whose new binding is not yet in scope there. It is now
the binding rustBindingAt finds in scope at the call, through the
block-scoped reader the typed-receiver path already uses.

A/B against main with fresh indexes: clap, dioxus, axum, serde, ripgrep
and gin are byte-identical; tokio has one call read more precisely, the
same target at a higher confidence (`entry.as_ref().handle()` on the
parameter `entry: NonNull<TimerShared>`). Two regression tests, both
failing on main. Full suite on the tested tree: 8,039 passed, 0 failed.

Follow-up 3 of the Rust receiver series (PR 2719's open list). Follow-up
2, an impl's type parameter in a return type read from the receiver's type
arguments, was measured and dropped: 84 value reads across six Rust repos
reach the case, most substituting a generic for a generic, an associated
type, or a head type already named, so about 10 to 20 calls would gain.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 01:15:31 +00:00
Colby MchenryandClaude Opus 5.5 bd62d9c061 fix(rust): a turbofish call reaches the function the turbofish wraps (#2720)
A Rust call written with a turbofish (`s.get::<u32>()`, `helper::<u8>()`,
`Pool::<u8>::new()`) kept its type arguments in the callee's name
(`s.get::<u32>`), which no resolution strategy reads, so every such call
was left unresolved. Both engines now name the function the turbofish
wraps and drop a path's type arguments (`Pool::<u8>::new` is
`Pool::new`), so the call resolves as it would without the turbofish: on
a typed receiver, on `self` or a field of `self`, and through an
associated-function chain. The kernel and wasm extractors emit the same
bytes; the parity fixture covers nested type arguments and a `->` inside
them.

A/B against main with fresh indexes, sites matched by position: clap
197 calls linked, dioxus 251 linked and 1 removed, axum 69, tokio 143,
serde 48, ripgrep 3; gin and express are byte-identical. About 680 of the
new links are right: typed method calls (clap's
`matches.get_one::<String>()` to `ArgMatches::get_one`, dioxus'
`self.query.new_query::<T>()` to `QueryEngine::new_query`) and generic
free functions (tokio's `channel::<T>()`, dioxus' `consume_context::<T>()`,
serde's `assert::<T>()`). About 22 are weak guesses at confidence 0.5 or
below, where an instance chain's method becomes a bare name, as it is for
the same call without a turbofish (dioxus' `.collect::<Vec<_>>()` to
`ServerFunction::collect`; main already makes 23 of those for
`.collect()`). The one removed link was wrong on main:
`serde_json::from_str::<T>(...).context(...)` is no longer guessed as
`DiffState::context`. The 54 sites reported as retargeted are positions a
chain's inner and outer calls share, where an edge was added beside the
existing one. Full suite on the tested tree: 8,037 passed, 0 failed.

Follow-up 1 of the Rust receiver series (PR 2719's open list).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 00:54:30 +00:00
Colby MchenryandClaude Opus 5.5 7324faf69f fix(rust): a call on a field of self reaches the method of the field's declared type (#2719)
A Rust `self.field.m()` call was resolved by the last segment of the
field's type name alone. `inner: multer::Field` linked to axum's own
`Field::name`, `std: std::time::Instant` to tokio's `Instant` (the calling
method itself), `inner: std::sync::Mutex<…>` to tokio's loom mock or async
`Mutex::lock`, and a field generic over the impl's `S: Store` to nothing.

The field's type is now read the way field values already are: from the
struct's declaration, through the wrappers, by the `use`s of the
struct's file. A struct's own parameter is known by the bounds that the
impl around the call gives the parameter it is instantiated with
(`struct Worker<S>` under `impl<S: Store> Worker<S>`). The method is the
one the typed receiver path picks. The plain-name reading stays only
where the field's type can't be read.

A call on a std or dependency value reaches a project trait's impl for
its type only when the impl is for that type. tokio's `impl Kill for
Child` in process/windows.rs is for its own `struct Child`, not
`std::process::Child`. The impl's type is read as the impl writes it:
gleam's `impl LowestVersion for pubgrub::Range<Version>` is for pubgrub's
`Range`.

A/B with fresh indexes against main on axum, serde, ripgrep, clap, dioxus,
tokio and gleam, every changed edge triaged: 361 calls linked that were
not (fields bounded by the impl's traits, dioxus's signal fields, trait
objects), 104 removed (calls on std and dependency fields, and 5 on tokio
values whose methods sit inside `cfg_*!` macros), 59 moved to the right
type of a shared name, 2,844 kept at a higher confidence; 62
fewer self-loops and none added. gin, serilog, flask and express are
byte-identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 23:04:17 +00:00
Colby MchenryandClaude Opus 5.5 a33040f786 fix(js,ts): an import of a package the repository declares itself links to that declaration (#2718)
A `declare module 'mammoth' { ... }` in a script .d.ts declares the module
outright, and TypeScript takes it before any file the specifier resolves to.
Nothing mapped a package name to a file in the repository, so an import of
such a package linked nothing, or, where the out-of-repo guard did not fire
(VS Code's extensions list no `vscode` dependency), a project namesake. A
bodiless function in an ambient context produced no node either, so
`convertToHtml` had nothing to link to.

- ambient-modules.ts: a token scanner for `declare module` blocks, a
  per-context index of the project's declaration files, and the lookup the
  import resolver makes: named, default (`export default`, `export =`),
  export-clause and namespace-member imports, by position in the block.
- The import resolver consults a script declaration before a file a path
  alias maps the specifier to (copilot's `paths` shim), and the import's
  module reference and namespace or default binding link the declaring file.
- Extraction, both engines: a function_signature under `declare`, in
  `declare global`, at a declaration file's top level or directly in its
  `declare module` body is a function; never in a namespace, nor in an
  ordinary module file's `declare module`, an augmentation.
- A module's function is reached from another file only through an import.
- A type merged across the files declaring one module is no single
  declaration when a receiver is typed.
- Sync re-opens the importers of a package a changed declaration file
  declares.

Augmentations, wildcard patterns, shorthands and test-fixture declarations
are left alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 21:35:02 +00:00
Eric MinishandClaude Opus 5.5 642c7bb3a5 fix(python): attribute calls follow the type the class body, a factory or a base gives the attribute (#1926)
Python calls through an attribute reach the method the code gives that
attribute: `self.sink.stop()` follows `self.sink = Sink()`, a `sink: Sink`
annotation, a typed `__init__` parameter, a factory's return type or what
every `return` in it builds, and a base class's `__init__` in another file.
Calls straight on a factory's result (`Client.from_env().send()`) resolve
the same way, and a sync re-opens these answers when the factory or base
they were read from changes. The native Python extractor now keeps a call's
receiver text as the wasm one does, so `self.data.append(1)` is never a bare
`append` that matches an unrelated project function. This includes all of
PR 1704.

Reworked from evidence-or-silence, which dropped about 3,000 correct Django
links (`self.client.get()`, `self.engine.render_to_string()`). The reader
now has three answers. A project class that has the method: that method.
Evidence that rules project methods out: no edge, for a builtin, a class
from outside the project built in a method or named by an annotation, an
attribute only an outside base can give (Django's `TestCase` in an app), or
a class without the method whose bases are all known. Nothing typed: the
call is guessed by the name of its last link, read as written, as any
receiver is, but only for an owner whose name holds the link's head noun
(`self.session` for `SessionBase`, not `database.group_shopping_lists` for
`GroupRepositoryAIProvider`) and never as the one method of a name; the
guess records the files the reader looked at, so a sync re-opens it too. A
class-body field an outside class builds (`author = models.ForeignKey(Author)`)
may be a descriptor, so it is no evidence. A chain through an imported
module (`os.path.exists()`) is the module's. In the shared scoring, a
receiver that spells an owner's whole name (`client` for `Client`)
outscores one that only shares its word (`RedisCacheClient`). A call
through an imported value's attribute chain (`app.jinja_env.filters.keys()`)
no longer links to the value's `jinja_env`.

A/B against main with fresh indexes, every changed edge triaged, sites
matched by position since the extractor now names these calls by their
whole chain. Django: 3,317 calls linked that were not
(`self.client.force_login()` to `ClientMixin`, `User.objects.create_user()`
to `UserManager`, `self.stdout.write()` to `OutputWrapper`), 127 moved,
nearly all to a more precise target (`self.site.register()` to
`AdminSite.register` rather than the `register` decorator), and 134
removed: main's bare-name links on builtin values (`value.lower()` to the
template filter, `.encode()` to a test password hasher) and calls on
expressions such as `(q1 | q2).order_by()`, which now go unresolved as they
do in the wasm engine. flask: 32 linked, 4 moved to `JSONProvider`.
fastapi: 58 linked. mealie: 740 linked. Guesses still wrong, as main makes
them for a plain receiver of the same name: fastapi's
`request.headers.get()` to a telemetry `_HeadersGetter`, and about 20 of
mealie's repository attributes to a controller. Go, C#, Ruby, Swift,
Kotlin, Java and TS repositories are byte-identical, except excalidraw's
5 `props.onChange()` calls, which move between two unrelated `Props`
interfaces. Indexing CPU: Django +7%, mealie +5%, fastapi flat; wall time
unchanged within noise. Full suite on the merged tree: 7,999 passed, 0 failed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 21:00:13 +00:00
Colby MchenryandClaude Opus 5.5 4286083a84 fix(rust): a for-loop variable or a tuple pattern's name has the type it is bound from (#2717)
A Rust `for` loop's variable, and a name a tuple pattern binds (`for (k,
v) in &map`, `let (tx, rx) = mpsc::channel(8)`), had no type the typed
receiver path could read, so calls on them fell to the older receiver
inference and the name strategies. tokio's 198 `tx.send(…)` calls on
channel senders went to the internal `chan::Tx::send`, and 100 calls on
the receivers to `chan::Rx`.

The scope reader now records what a `for` loop iterates, and gives the
plain names of a top-level tuple pattern their place in it. A loop's
variable is an item of what it iterates: a std sequence's element, a std
map's `(K, V)`, a slice's, a std iterator's or an `impl Iterator<Item =
T>`'s item, through `&` and the pointers that deref to what they hold.
The std adapters that keep the items (`.iter()`, `.rev()`, `.skip(n)`)
or project them (`.keys()`, `.values()`, `.enumerate()`) are looked
through only where what they are called on is read, so a project method
of one of those names is read from its own signature. A tuple pattern's
name is the matched tuple's field at its place. A field access
(`self.items`, `opts.config`, `pair.0`) has the type the receiver's
struct declares for the field.

A free function called through a module that a `use … as` renames was
looked up by the alias: tokio's loom tests' `oneshot::channel()` under
`use crate::runtime::tests::loom_oneshot as oneshot` read the return type
of `sync::oneshot::channel`. The path's head now goes through its `use`,
and the project's modules are walked along it.

A/B with fresh indexes against main on axum, serde, ripgrep, clap, dioxus,
tokio and gleam, every changed edge triaged: 394 calls linked that were
not, 86 removed (calls on std or dependency values, tokio values whose
read and write methods come from macro-declared extension traits, derived
`Clone`s, fields that hold functions), 404 moved to the declared type's
method, 625 kept at a higher confidence. gin, serilog, flask and express
are byte-identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 20:33:36 +00:00
Eric MinishandClaude Opus 5.5 e043efbc84 fix(resolution): a call through a class name reaches that class, not one whose name contains it (#1935)
A call through a class name, `Logger.log()`, or through a receiver named
after one, `logger.log()`, now binds to that class itself: its own
method, else the nearest one a declared supertype has. Before, the
method was any one in the class's file whose qualified name contained
the class's name, so a `FileLogger` declared first took every
`Logger.log()`, and Kotlin's `Foo.create()` went to `FooBar`'s.

Reworked from the exact-owner rule alone, which lost inherited methods
(Django's `form.is_valid()` on `Form(BaseForm)`, Alamofire's protocol
methods). Also in this change:

- A class declared inside a function is named only there: a call from
  anywhere else no longer reaches a class a test declares in its body,
  through the class name, the receiver's name, or the method-name
  fallback. Those methods were never in the running, and the one method
  such a filter leaves is still only a guess by the receiver's name.
- Production code never names a test suite's class, as the method-name
  fallback already held: `manager.check()` in Django's
  `Model._check_managers` is no test app's `Manager` model.
- A Python base built by a class's factory, Django's
  `class Manager(BaseManager.from_queryset(QuerySet))`, names that class
  as the supertype, besides the factory's arguments.

A/B against main with fresh indexes, every changed edge triaged. Django:
67 links added (model instances reaching `Model.save`, inherited lookups
and expressions), 40 removed (links into test-local classes from outside
their function, and one polymorphic manager call), 263 retargeted:
about 170 to the right class (`user.has_perm` to `PermissionsMixin`
rather than `AnonymousUser`, `parser.parse` to the template `Parser`
rather than `MultiPartParser`, `field.get_lookup` to
`RegisterLookupMixin`), about 90 from one guess to another, and about
20 worse, where a receiver named after a model holds something else
(`session.save()` on a session store now guesses the `Session` model).
flask: one fix (`app.get` to `Scaffold.get`), 3 wrong links removed.
Alamofire: one fix and 3 protocol-typed locals that now guess the
`Adapter` class. zod, excalidraw and spring-petclinic: higher
confidence on the same targets. gin, cobra, express, eShopOnWeb and
jekyll are byte-identical. Full suite on the merged tree: 7,908 passed,
0 failed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 19:55:52 +00:00
Colby MchenryandClaude Opus 5.5 f76021db22 fix(rust): a local holding a method call's value has the type the method returns (#2716)
A Rust local bound to a method call (`let server = b.port(80).build()`,
`let s = cfg.load()?`, `let conn = c.connect().await`, `if let Some(c) =
arg.get::<Completer>()`) had no type the typed receiver path could read,
so calls on it fell to the name strategies. Those strategies did not read
`?` on a returned `Result`, a `self` method's value or a turbofish, and
picked a same-named method of another type.

The value is now the return type of the method the call reaches on its
receiver's type, read from that method's signature. A receiver is a local,
`self`, or another such call. `.await` hands out an `async fn`'s return
type (read off the source: the extractor's async flag is never set for
Rust) or an `impl Future`'s `Output`, and an `async fn` not awaited is a
future, left unread. A method's own type parameter is the call's turbofish
argument. A parameter the call doesn't write stays opaque: `Standard<W>`
is a `Standard`, but a value that is `W` itself is unread rather than its
bound. A trait's provided method returning `Self` gives the receiver's
own type.

A type with two impls of one trait, `Encoder<Bytes>` and `Encoder<&[u8]>`
or `Service<IncomingStream>` and `Service<Request<B>>`, was ambiguous for
a typed call. The impl whose trait argument is the call's first
argument's type now wins.

A/B with fresh indexes against main on axum, serde, ripgrep, clap, dioxus,
tokio and gleam, every changed edge triaged: 536 calls linked that were
not, 69 removed (calls on std or dependency values a method returns, and
tokio values whose read and write methods come from macro-declared
extension traits), 50 moved to the declared type's method, 565 kept at a
higher confidence. gin, serilog, flask and express are byte-identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 19:41:15 +00:00
Colby MchenryandClaude Opus 5.5 23b5ca1b43 fix(rust): a call on a local reaches the method of the type it is declared with (#2715)
A Rust method call on a local variable or a parameter (`opts.build()`) was
matched by the method's name whenever the receiver's declaration wasn't one
of two unscoped line patterns. dioxus's `Commands::Build(opts) =>
opts.build()` lost its link once the build module's `BuildRequest::build`
shared the name, and calls on std or dependency values linked to project
namesakes: `cx.waker()` on a `Context` to tokio's `CachedParkThread::waker`.

The receiver is now typed by the binding the call sees. The new
src/resolution/rust-scopes.ts reads each file's parameters, closure
parameters, lets, if-let / while-let / let-else, for and match-arm bindings
with block scope. A binding's type is its written one, the value it is
built from, or the variant or struct field its pattern takes. The method is
the one Rust picks on that type: inherent first, then a trait impl whose
trait is in scope, then a trait's provided method (a blanket impl only where
its bounds are met), then Deref. `&`, Box, Rc, Arc, Pin, Cow and the lock
guards are looked through, their own methods kept (one list with the
self-call path's), and a project impl on the wrapper itself answers before
what the wrapper holds, as it does for `self`. `impl Trait`, `dyn Trait` and
bounded generics reach the trait's method or a supertrait's. Among types
sharing a name, the one its path names through the project's modules wins,
the path written or bound by a block-scoped `use`, else the one that has
the method. A std, dependency or primitive type reaches only a project trait
impl in scope, and not one that hands the call on to the type's own method
of that name.

A binding that writes no type leaves the call to the name strategies, which
no longer take a project type from another declaration of the name. A
trait's own methods carry no `pub`: isVisibleAcrossFiles lets them through
for the calls this path resolved, and name guesses stay out as before.

A/B with fresh indexes against main on axum, serde, ripgrep, clap, dioxus,
tokio and gleam, every changed edge triaged: 4,645 calls linked that were
not, 887 removed (calls on std or dependency values, project types without
the method, a wrapper's own methods), 459 moved to the declared type's
method, 3,669 kept at a higher confidence. gin, serilog, flask and express
are byte-identical. The new code costs 0.6 to 0.9 s of CPU per full index
of tokio, dioxus or gleam.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 18:55:06 +00:00
Colby MchenryandClaude Opus 5.5 f5158fac02 fix(rust): a name a glob use brings in is an item the globbed module passes on (#2714)
dioxus's examples write `use dioxus::prelude::*;` and then `Element` or
`use_signal(...)`, which the prelude re-exports from dioxus-core and
dioxus-hooks: `pub use dioxus_core::{Element, ...}`, and `pub use
crate::hooks::*` through `pub use dioxus_hooks as hooks;`. The glob rule
(rustGlobCovers) only took an item declared in a file named like the glob's
last segment, so all of those went unlinked, while `use super::*` took
anything in the parent's directory and a glob over another crate's `types`
or `error` module took this crate's file of that name.

A name only globs bring in now goes through the module walk of #2702:
rustGlobItems walks each of the file's globs from the module it is written
in and looks the name up there. Two additions to the walk:
- a step through a module its parent passes on under that name
  (`pub use dioxus_hooks as hooks;`, `pub use dioxus_signals::{self, *}`);
- Rust's visibility for globs: seen from inside a module (`use super::*`) a
  glob takes everything; from anywhere else only what the module declares
  `pub`, `pub use`s or `pub` globs. dioxus-hooks' `pub use use_effect::*`
  does not pass on that module's own `use dioxus_core::*`, so the prelude
  never brings in dioxus-core's `Ok` function; without this rule 146 std
  `Ok(...)` calls in dioxus's examples linked to it.
A glob that brings the name in settles it, so one the walk can't follow
(a macro's module, a #[path] module) matters only when none does; then the
old module-name rule answers as before.

A/B with fresh indexes, main 505c355b and again on 818e7803, every changed
edge triaged: gleam 2053 links added (its `use crate::ast::*` and
`pretty_arena::*` names), 32 moved to the right item, 44 wrong ones gone
(std `Result` taken for the crate's alias through a test module's
`use super::*`, a `compile` parameter); dioxus 2329 added (the prelude's
hooks, signals, stores and rsx/html items), 24 dispatch fan-outs updated by
new implements edges, 7 wrong ones gone; tokio 6 added (`tokio::time::*`),
3 moved to a same-file enum; axum 2 added, 1 wrong gone; serde, ripgrep and
clap only confidences or nothing. No right link lost; express, gin, flask
and leveldb byte-identical. Cost: about 0.24 s of CPU per full index of
dioxus, 0.02 s on gleam.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 18:33:42 +00:00
Colby MchenryandClaude Opus 5.5 e2aa5d205d fix(rust): a same-file path's inline module is read from its braces, not its indentation (#2713)
PR #2711 and PR #2702 landed a way each to tell which inline `mod` a
place in a Rust file is in. #2711's isInRustInlineModule scanned lines
upward for `mod <name> {` and trusted rustfmt's indentation to find where
the block closes. #2702's rustModBlocks scans the braces, skipping
strings, raw strings, char literals and comments, and keeps the result in
rustUsesOf. isInRustInlineModule now asks rustBlockAt for the innermost
block around the candidate, as rustDeclaredItems does, so there is one
answer to that question.

What changes:
- An item written without indentation inside `mod flat { … }` is found
  through `flat::Flat`. The indentation rule rejected it.
- `outer::Shell` is only `outer`'s own `Shell`, not one declared in a
  `mod deep` inside it. The upward scan accepted any item below the
  `mod outer {` line.
- A file whose braces don't all close says nothing, as it does for the
  `use` walk of PR #2702.

The A/B (main 818e7803 vs this change; axum, tokio, clap, serde,
ripgrep, gleam, dioxus, and the gin, serilog, flask and express
controls) is byte-identical. rust-path-same-file.test.ts gains the two
cases. The `flat::Flat` one fails on main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 18:31:06 +00:00
Colby MchenryandClaude Opus 5.5 818e780336 fix(rust): a trait's associated type named <T as Trait>::X or bound as Trait<X = …> links only to what it names (#2712)
A trait's associated type is named in two more ways than `Self::X`, and
neither names a module item:
- Through a qualified self, `<T as Trait>::X`. isRustNameInScope took any
  associated item of the name for it. axum's `<L as Listener>::Io`, with
  `L` a generic, went to `ReadyListener`'s `type Io`. A trait's own
  `fn call(…) -> <Self as HandlerCallWithExtractors<T, S>>::Future;`
  went to an impl's `type Future` further down the file.
- As a binding in the trait's arguments, `Iterator<Item = &str>` or
  `Readable<Storage = UnsyncStorage>`. The name was matched like a bare
  type. axum's `impl Iterator<Item = &str>` went to a same-file
  `enum Item`, and dioxus's `Storage = …` bindings to the `Storage` trait.

Fix, in isRustNameInScope:
- isRustAssocBinding: a name followed by `=` or `:` inside an unclosed
  `<` on its line is a binding or bound, and links nothing. The trait's
  own associated type is no node.
- rustQualifiedSelf reads `<T as Trait>::X` (and `<T>::X`) as its self
  type and trait, each as the last segment of its path:
  - `Self` means `Self::X`: the enclosing impl's own item, nothing in a
    trait's body;
  - a generic means no impl's;
  - a type means its impl of that trait (the candidate's
    `Owner::Name` must match, and its impl header's trait too).

Tests: rust-assoc-qualified-self.test.ts, 3 tests, on both engines; all
3 fail before the fix, and switching off either rule fails its tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 18:11:39 +00:00
Eric MinishandClaude Opus 5.5 6fe9dc1266 fix(python): resolve imports from the package roots the project has (#1921)
Python imports now resolve the way the interpreter finds modules: from
the package roots the project has, not by any file whose path merely
ends in the module's name. A project root is the repository, each
service with its own pyproject.toml, setup.cfg or setup.py, its src/,
and the package directories its build config declares; a local root
answers only importers within its reach, so a test fixture's copy of a
package never becomes the application's. Relative imports climb from
the importer's own package.

Before, `import json` or `from datetime import date` could link to a
project file or function of that name, and a src-layout package like
flask's was found through a test fixture named flask.py. On flask,
fastapi, mealie and django, relative imports now resolve (about 2,200
links added), stdlib and third-party names stop linking to project
namesakes (about 1,300 wrong links removed), and imports of a module's
function land on the function rather than its file. A sync re-opens
what an added or removed module, an edited __init__.py or a changed
package root can move. Indexing CPU time is unchanged.

Merged with main: a function-local `import a.b as X` (PR 2607) now uses
the root-aware lookup, the outside-module receiver check (PR 2606)
passes the importer, and plain imports are read at module level only,
since the mappings apply to every function of the file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 18:07:07 +00:00
Colby MchenryandClaude Opus 5.5 505c355be3 fix(rust): a name written through a path is what the path names, not a same-file namesake (#2711)
A Rust name written through a path is an item of what the path names: a
module (`ast::Pattern`, `io::Error`), a type (`Source::Record { … }`) or a
crate (`mio::Interest`, `std::ffi::OsStr`). isRustNameInScope accepted any
candidate in the file the path is written in, whatever the path said, and
a std path only when the candidate was in another file:
- clap's `std::ffi::OsStr` linked to its own `struct OsStr`, and
  `io::Error`, `fmt::Error` and `fmt::Result` in `error/mod.rs` to its own
  `Error` and `Result`;
- tokio's `mio::Interest` linked to its own `Interest`, and
  `std::time::Instant` to its own `Instant`;
- serde's tests' `Token::Struct { … }` literals linked to their own
  `Struct`s, and serde_derive's `syn::Path { … }` to a `Default::Path`
  variant;
- gleam's `ast::Pattern` linked to the `enum Pattern` beside it, and
  `BinaryBuilderPosition::RecordAttribute { … }` to another enum's
  variant of that name.

Fix, in the path branch of isRustNameInScope:
- A path from std, core, alloc or a crate the manifests depend on names
  nothing of the project's, in any file, unless the project is that crate
  or a `use` in the file binds the root.
- The same-file shortcut is gone. A candidate is the path's when its
  file is the path's module, its qualified name holds the segment (a
  type's variant or method), or it sits in an inline `mod` of that name
  in the file (isInRustInlineModule; rustfmt closes the block at the
  indentation it opens at).
- Through a module a `use` binds, a candidate must be in a crate that
  `use`'s path can reach (rustUseCanReach, as for a bare name): tokio's
  `io::Handle` under `use crate::runtime::io` is tokio's own, not
  tokio-test's `io.rs` `Handle` (a dev-dependency).

Tests: rust-path-same-file.test.ts, 6 tests, on both engines; 5 fail
on main (the sixth is the inline-module guard), and the crate-reach one
also fails with only that rule switched off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 17:38:32 +00:00
Colby MchenryandClaude Opus 5.5 5e72a43b3c fix(rust): a name a use brings in is the item of the module its path walks (#2702)
tokio's signal/mod.rs writes `use crate::sync::watch::Receiver;` and
tokio-stream `use tokio::sync::watch::Receiver;`, and both linked their
`Receiver<()>` to the `Receiver` of net/unix/pipe.rs. Every `Receiver` in
the crate read as in scope once the file imported one, and the first won.

isRustNameInScope now walks the modules a `use` path names, from the module
the `use` is written in: crate::, self::, super::, a module declared there
(2018 paths), the crate's own name, or a project crate its manifest depends
on. It looks the item up there as Rust does: declared in that module (not
inside a function or an inline module), else brought in by the module's own
`use` of that name, macro-wrapped ones included (tokio's `cfg_sync! { pub use
mutex::{Mutex, ...}; }`), else by its globs. A module declared inline, like
`pub mod futures { ... }`, is found by its braces. A candidate the path does
not name is out of scope, so every strategy, the framework name rule
included, lands on the item the path names.

What the walk can't follow says nothing, and name matching answers as
before: a module with no file, one a path attribute puts elsewhere, a glob
over an enum, a file whose braces don't balance. A module that holds no
such item the index knows, because a macro declares it or another crate
does, leaves nothing in scope. A `use` of the name that yields no item
doesn't hide the globs: clap's `pub use clap_derive::ValueEnum` is a derive
macro, and the trait comes in through `pub use clap_builder::*`.

A/B with fresh indexes, main 5530676f against this branch, every changed
edge triaged: tokio 233 links move to the item the path names and 78 wrong
links go (the real items are declared inside cfg_*! macros, or are std's
io::Result); axum 6 move; clap 20; ripgrep 49 move and 2 go (termcolor's
ColorChoice); gleam 404 move; dioxus 58 move. No right link is lost, and
527 more keep their target at a higher confidence. serde changes only
confidences; express, gin, flask and leveldb are byte-identical. The new
lookups cost about 0.2 s of CPU per full index of tokio or gleam.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 17:36:35 +00:00
Colby MchenryandClaude Opus 5.5 5a7a4347a6 fix(rust): a call on what as_mut() hands back reaches the receiver's declared type (#2710)
A Rust call written x.as_mut().m(..) or x.as_ref().m(..) reaches the
resolver by its bare name, so it was matched by name alone. tokio's
self.delay.as_mut().reset(now) on a Pin<Box<Sleep>> field linked to the
calling Interval::reset, its this.timer.as_mut().as_pin_mut() on an
Option to a stream's as_pin_mut, reader.as_mut().poll_read(..) on a
generic R to BufReader::poll_read, and dioxus's
self.child.as_mut().map(..) and self.server.as_mut().filter(..) on
Options to its own GenerationalRefMut::map and Store::filter.

Such a call is now read from the call's own chain and resolved on the
receiver's declared type as as_mut passes it on: a Pin<P> re-pins what
it points at (Rust's lookup through the Pin from the previous change),
a Box, NonNull, Rc or Arc lends what it holds, an Option stays an
Option, whose methods are std's, unless ? takes the value back out
(self.client.as_mut()?.next()), and a type parameter or another std or
dependency type gives no project method. A field is read from its
struct's declaration, also through pin-project's let me = self.project()
(a #[pin] field projects to Pin<&mut T>), even when a pin_project! macro
wraps the struct; a local or parameter from its own declaration, which
now also reads let x: T = .. annotations. A receiver whose type isn't
written where it can be read, a pin! local or a call's result, is left
to the name strategies as before.

A/B on tokio, axum, clap, serde, ripgrep, gleam and dioxus: 33 wrong
links removed, 7 moved to the right method, 13 right links added, none
lost, and 28 same-target links now resolved from the declared type
instead of by name. axum, clap, serde and the controls express, gin,
flask and leveldb are byte-identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 17:32:25 +00:00
Colby MchenryandClaude Opus 5.5 a31bf2f476 fix(c): a macro alone below an #include <…> no longer hides the next declaration (#2709)
blankLoneMacroLines blanks a macro alone on its line, like
FMT_BEGIN_NAMESPACE, before C and C++ parse, unless the line above ends
in an operator it could be an operand of. A directive like
`#include <glib/gtypes.h>` ends in `>`, so GLib's G_BEGIN_DECLS below it
stayed, and the parser read `G_BEGIN_DECLS typedef struct _GBytes
GBytes;` as one broken declaration. The first declaration of nearly
every GLib header went missing (GBytes, GError, GHashTable, GList,
GQuark, the GIOCondition enum), a few headers grew junk functions (one
named `enum` swallowing a whole header), and the parse error kept the
file off the native kernel.

Objective-C already read a directive as ending at its line (#2691,
directivesEnd). C and C++ now do too, so the flag goes: a directive
continues only through a trailing backslash. The kernel takes C and C++
input from the hoisted preParse, so it needs no change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 17:23:20 +00:00
Colby MchenryandClaude Opus 5.5 90e2456b43 fix(cpp): a bare call on a parameter, local lambda or functor links nothing, not a function elsewhere of its name (#2708)
A C++ call written by its bare name on a name the calling function binds
itself (a parameter, a lambda's parameter, a local lambda or functor, a
range-for variable) calls that local: C++ lookup finds it before any function
or method of that name. The name strategies linked such calls by name:
protocolbuffers/protobuf's code generators' `format(...)` on their
`Formatter& format` parameter went to the conformance suite's
`TestResult::format`, and facebook/rocksdb's tests' local
`auto key = [](int i) {...};` calls to `ArenaWrappedDBIter::key`.

isCppLocalCallableCall, checked in matchReference beside the JS/TS rule,
reads the caller's declarations of the name: its parameters, a declaration
that starts a statement (`auto x = ...`, `T x = ...`, `T x(args)`, a later
name in a list), a range-for variable or a lambda's parameter, the nearest one
in a block still open at the call deciding. A local type or a function the
body declares is not a local callable (the call constructs it or calls the
function), and an expression that reads like a declaration (`a && f(x)`)
declares nothing. A local's type may carry a function type in its template
arguments (`std::function<void()> done, failed;`), so the first name of such
a list counts as the later ones do, and the #2698 test of that list now
expects no edge for either. The looser isCppLocalName used by implicit-this
is unchanged.

A/B on main 5530676f: protobuf 269 and rocksdb 51 edges removed, all calls
on locals, nothing else changed; glog, leveldb, breakpad and redis
byte-identical. The check costs 3.2 s of about 450 s CPU on protobuf.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 17:14:25 +00:00
Eric MinishandClaude Opus 5.5 01cd287786 fix(express): only the handler argument decides whether a route is inline (#1957)
An Express route keeps its named handler when a middleware before it
holds an arrow function, like rateLimit({ keyGenerator: (req) => req.ip })
or (req, res, next) => next(). Only the last argument decides whether
the handler is inline, and strings, regex literals and type arguments
no longer end an argument or a body early. The Express, route and
framework suites pass (757 tests); on express itself three named
handlers gain their link.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 17:13:07 +00:00
Eric MinishandClaude Opus 5.5 9dc3bc3adc fix(resolution): honour TypeScript type modifiers on imports and re-exports (#1955)
Import and re-export lists are now read with TypeScript's `type`
modifiers (`export type { A } from`, `export { type A } from`,
`export type * from`), comments inside the list, ES2022 string names
and JSDoc @import tags. A type re-exported from a package, like vben's
`export type { VxeGridProps } from 'vxe-table'`, no longer links to a
project type of the same name, and type imports link through the
import rather than by name. The import-related suites pass (609 tests)
and indexing CPU time on vben is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 17:12:06 +00:00
Eric MinishandClaude Opus 5.5 8afb970a23 fix(ts): an awaited call with type arguments links to its callee (#2595)
tree-sitter-typescript parses `await f<T>(x)` as a call whose callee is
`await f`, so the reference was named `await f` and matched nothing.
Both engines now peel the await, skipping a comment after it, and the
reference sits where the callee starts, as for `await f(x)`: on the
call node, which starts at await, the checks that read the source at a
call's position missed the callee, and a package-imported
`await get<T>()` linked to a project class's static get.

A/B: excalidraw +2 (compressData, decompressData), vben +6, zod and
bulletproof-react unchanged.

Fixes issue 2586.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 17:11:12 +00:00
Eric MinishandClaude Opus 5.5 63476df6a6 fix(react): JSX at module scope, in a top-level const or in a test callback renders from where its calls are attributed (#2611)
JSX outside every function now renders from what a plain call there is
attributed to: a top-level value, or the file. Ownership is by line,
so no tag counts twice. Merged with main's destructuring rules (PRs
2676 and 2677): an owning parent takes a name as its local by its own
lines or by the file's top-level destructuring, and function parents
keep main's scope rules. The viewer's shared-chrome count no longer
counts a file as a render parent.

A/B: excalidraw +132 and bulletproof-react +38 render sites (tests to
the components they render, entries to their root apps, stories to
their components), none removed.

Fixes issue 2587.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 17:08:28 +00:00
Eric MinishandClaude Opus 5.5 b95b316860 fix(sync): git sync hooks refresh the project they were installed for (#1965)
Git runs hooks from the top of the work tree, so a hook running a bare
`codegraph sync` never reached a sub-project's index. Each hook block
now names the project root it syncs, shell-quoted, one block per root,
placed right after the shebang so a user hook that ends in exit cannot
skip it. A hook in another language is left untouched and reported,
and a block from an older install counts as the top-level project's. A
project path with a line break is refused, since the root is written
on a comment line in the hook.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 17:07:25 +00:00
Eric MinishandClaude Opus 5.5 e3f23cc4bc fix(go): a local's scope ends at its case clause, and a func-typed result's parameters are not locals (#2597)
Main's gate for a Go call to a local func value or parameter that
shadows a package function (PR 2658) reads scopes through the Go scope
reader, and two of its readings were too wide. A name declared in one
case of a switch or select stayed in scope in the clauses after it, and
the parameter names inside a func-typed result, like
func() func(run string), *func(...) or chan<- func(...), were read as
the function's own. Either made a later call to the package function
look shadowed and drop its link.

The PR's own earlier shadowing check is left out: the gate already
covers every case its tests pin. Go A/B on gin, cobra, etcd, prometheus
and hugo is byte-identical.

Fixes issue 2580.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 17:06:31 +00:00
Eric MinishandClaude Opus 5.5 23ff26be68 fix(python): an aliased module import inside a function links its calls (#2607)
`import a.b as X` written inside a function binds X there, ahead of any
module-level binding of X, so X.f() reaches a/b.py's f, as the same
import at module level already did. An undotted alias is left alone,
since an optional dependency's name can match any project file.
mealie's lifespan_fn now links init_db.main; the other repos are
unchanged.

Fixes issue 2600.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 17:04:29 +00:00
Eric MinishandClaude Opus 5.5 4f222a3d65 fix(python): the module of a from-import no longer links a same-named variable (#2603)
The module of a Python `from X import ...` names a module or package,
never a symbol, so `from app import email` no longer links the
importing file to a project variable named app, like a FastAPI or
Flask app = FastAPI(). A from-import module that resolves to anything
but a file or module links nothing. On five Python repos this removes
12 wrong import links and nothing else.

Fixes issue 2584.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 17:02:50 +00:00
Eric MinishandClaude Opus 5.5 e48565067e fix(python): a call through a module-level instance links its class's method (#2608)
A Python call through a module-level instance of the same file, like
_svc.delete(obj) after _svc = SceneService(), now links to the method
its class has or inherits, as a method value through that global
already did.

On django and fastapi: 50 correct links added, 42 moved to the right
class (DEBUG_ENGINE's Engine, the dispatch tests' Signal), confidence
raised on about 400 more. CPU time on django is unchanged.

Fixes issue 2582.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 17:01:54 +00:00
Eric MinishandClaude Opus 5.5 cc1eb1f942 fix(python): a call on an imported outside module no longer links a same-named project method (#2606)
A Python call on a module the file imports from outside the project,
like subprocess.run(...) after import subprocess or sp.run(...) after
import subprocess as sp, is that module's function, never a project
class's method of the same name. The receiver must be bound only by
such imports: an assignment, loop or tuple target, a global write in a
function, or a fallback class or def of the name keeps the old lookup.

On django, mealie, fastapi and flask this removes 168 wrong links
(mock.call, time.sleep, sa.text, unicodedata.normalize, click.command)
and adds none. CPU time on django is unchanged.

Fixes issue 2599.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 17:00:22 +00:00
Eric MinishandClaude Opus 5.5 c8f154b9f4 fix(explore): pin the dominant-file aggregation to an edges-first plan (#2575)
The dominant-file aggregation behind codegraph_explore could let SQLite
drive the query from the nodes table, which on a large index took
seconds to a minute. The query now pins an edges-first plan. On four
indexes it returns the same rows, 4 to 12 times faster.

Fixes issue 2571.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 16:59:07 +00:00
Colby MchenryandClaude Opus 5.5 b5553e238a fix(rust): an impl's associated type or const no longer takes a bare name or a module path (#2707)
A `type` or `const` written inside an `impl` belongs to the type: Rust
reaches it only through a path like `Self::Err`, `<T as FromStr>::Err` or
`T::Err`, never by its name alone, and no module path or `use` names it.
Both extractors put it at file scope under its bare name, so the resolver
could not tell it from a module's own alias:
- a same-file `Err(…)` call linked to a `FromStr` impl's `type Err`, and
  serde's `Ok(…)` calls to a serializer's `type Ok`;
- axum's routing tests' `Response` linked to the `type Response` of
  `routing/mod.rs`'s `impl Service`, a private item the modules inside
  it can see;
- `io::Error` and `std::error::Error` linked to the `type Error` of an
  impl in the same file, and serde's `use crate::de::Error` to one in
  `de/mod.rs`.

Fix:
- Both extractors (rustImplItemOwner, and the kernel's impl_item_owner)
  qualify an item written directly in an impl by the impl's type, as they
  already qualify its methods: `ValueHint::Err`, `Limits::MAX`. A type no
  single name stands for keeps the impl's spelling, whitespace collapsed:
  `(A, B)::First`, `[u8]::Out`, `u64::WIDTH`.
- isRustAssociatedItem: a Rust type alias or const with a qualified name.
  Nothing else of those kinds gets one: an inline module adds no prefix,
  a function body's items aren't indexed, and a trait's default `type`
  is already its trait's.
- isRustNameInScope: such an item never matches a bare name, and matches a
  path only as `Self::X` in its own impl or as `<…>::X` (unchanged).
- resolveRustPathReference never picks one for a `crate::m::X` path.

Tests: rust-associated-item-scope.test.ts, 6 tests, on both engines; 5
fail on origin/main. The kernel parity torture fixture gains associated
items on a tuple, a slice reference and a primitive.

A/B on fresh indexes, origin/main 5530676f vs this change. Every changed
edge was triaged; no right link was lost:
- axum: 8 wrong links gone, 14 moved to axum-core's `pub type Response`.
- tokio: 46 wrong links gone.
- clap: 12 wrong links gone, 7 of them `Err(…)` calls.
- serde: 98 wrong links gone; `First<…>`/`Second<…>` now reach the
  module's aliases.
- ripgrep: 54 wrong links gone; `impl Captures for RegexCaptures` (3)
  now implements grep-matcher's `Captures` trait.
- gleam: 5 wrong links gone, 10 moved to the right target, 1 moved from
  one wrong target to another.
- dioxus: 71 wrong links gone, 40 moved to the right target, 3 moved from
  one wrong target to another.
- Controls (gin, serilog, flask, express) byte-identical; indexing time
  unchanged within noise.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 16:57:03 +00:00
Colby MchenryandClaude Opus 5.5 6c49bfa667 fix(go): a call through a variable declared with another package's type reaches that type's method (#2706)
Go receiver inference read a parameter's type only when the line
patterns could: a lowercase name of the caller's own package (`s
*server`) or a capitalized one (`lg Logger`). A parameter declared
through a package, like `client rest.Interface`, `c clock.PassiveClock`
or `t *testing.T`, had no type, so a call on it was matched by name:
kubernetes' `client.Patch()` reached `RESTClient::Patch` instead of the
interface, `c.Now()` an unrelated `Clock`, and every `t.Errorf(…)` a
test helper's `T` interface. Only standard method names (PR 2664) and
receivers named like an import (PR 2448) asked the scope reader.

inferLocalReceiverType now asks the scope reader first for every Go
receiver. The declaration in scope (a parameter, a result, a name
grouped with others, a `var (…)` spec, a function literal's parameter)
gives the named type it writes, as GoReceiverDecl.declared, ahead of
the patterns, which scan back with no block scope. matchMethodCall,
matchMemberFunctionRef and matchGoFieldChainCall all use it:
- a project type resolves in its own package, through embedding and
  aliases;
- a type from outside the project, a predeclared one or an alias of
  either (goHasNoProjectMethods) reaches none of the project's methods,
  and neither does a type literal;
- a qualifier none of the file's imports names keeps the old lookup by
  name.
Standard method names keep the rules PR 2664 gave them. Any other name
the declared project type lacks goes on to the name strategies, as on a
receiver of unread type. The block that typed only receivers named
like an import is subsumed and removed, with the field it read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 16:38:24 +00:00
Colby MchenryandClaude Opus 5.5 70783f31f1 fix(objc): a message to an ivar or a property reaches the class it is declared as (#2703)
An Objective-C message to a local or a parameter was typed by its
declaration (`FMResultSet *rs` makes `[rs close]` FMResultSet's). One to
an instance variable or a property was not, since that declaration sits
outside the calling method: in a header's @interface, an @implementation's
ivar block, a superclass, or synthesized for a property. The receiver's
name decided instead. ObjFW's tar tool's `[_archive nextEntry]` reached
OFLHAArchive::nextEntry, and React Native's `[_displayLink invalidate]` on
a CADisplayLink reached RN's own RCTDisplayLink::invalidate.

- objcFileMembers reads what each class declares, per file: the ivar
  block right after an @interface head (seen by subclasses; the head may
  go on over several lines, a protocol list on a line of its own), a
  class extension's or an @implementation's ivar block (the class's own),
  @property declarations, including ObjFW's two-line ones and IBOutlet
  ones, and @synthesize lines. Types take protocol lists, type arguments
  and ObjFW's OF_GENERIC(...).
- objcMemberReceiverType, after the local inferrer, follows the language.
  `self.name` is a property of the class or a superclass. A bare name is
  an ivar: the class's own, one its own property synthesizes (`_name`,
  never a category property's), one @synthesize names, or a superclass's
  @interface ivar. A bare name is never a property's: `arcs` in
  `initWithArcs: (...)arcs` is the parameter. A capitalized bare name is a
  class, and `self.name` needs a property node, which skips most lookups.
  The type then takes the typed path (resolveMethodOnType at 0.9), and a
  class from outside the project has no project method, as for locals.
- objcClassFiles: when more than one file declares `@interface Name`
  itself, those are namesakes in separate targets, and only the ones in
  the calling file's directory count. SDWebImage's TV demo ViewController
  was otherwise typed by the macOS demo's `NSImageView *imageView1`.
- localReceiverTypePatterns('objc'): a local's or a parameter's type may
  carry OF_GENERIC(...), like `OFMutableArray OF_GENERIC(OFNumber *) *arcs`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 16:35:36 +00:00
Colby MchenryandClaude Opus 5.5 f2f5dee044 fix(rust): a call through a Pin, Rc or Box self reaches the method Rust picks, inherent first (#2701)
A Rust method called through a self declared Pin<&mut Self>, Box<Self>,
Rc<Self> or Arc<Self> was typed by its wrapper. tokio's
self.poll_flush(cx) in impl AsyncWrite for File went to its blanket impl
AsyncWrite for Pin<P>, whose poll_flush would need a Pin<&mut Pin<...>>,
and nearly every other such call linked to nothing: tokio's
self.poll_write_priv(..) and self.get_pin_mut(), dioxus's
self.in_scope(..) through self: &Rc<Self>.

Such a call is now looked up as Rust does: the type's own method taking
self through that wrapper first, then the wrapper's own (Pin::get_mut,
Rc's clone: std's, which the index doesn't hold, or a project impl on the
wrapper taking &self), then the type's other methods by deref. A value
declared Pin<&mut T> is looked up the same way on T, so
read.poll_read(..) on a generic R no longer reaches the Pin<P> impl.

A type's inherent method comes before a trait's of the same name, as in
Rust: on a path (tokio's Arc::from_raw(ptr) is std's, not its impl Link
for Arc<ScheduledIo>'s), on a typed value, and through self, where each
of several trait impls for one type calls its own block's method. In a
trait impl, fn wake(self) { self.wake() } hands the call on to the
type's own method, never back to itself, and (self.f)(x) calls what a
field holds, no method. A method called on a tuple, serde's
(self.ip(), self.port()).serialize(s), is no named type's.

A/B on tokio, axum, clap, serde, ripgrep, gleam and dioxus: 30 wrong
links removed, 13 moved to the right target, 74 right links added, none
lost. express, gin, flask and leveldb are byte-identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 16:32:16 +00:00
Eric MinishandClaude Opus 5.5 8beab37c46 fix(resolution): a call that is a braceless if/while/for/do body is a bare call (#2592)
A receiver-less JS/TS call written as the body of a braceless if, while,
for or do, like `if (e.key === "Escape") setOpen(false);`, is now read
as a bare call. In the JS family a receiver ends in `.` or `?.`, so a
`)` right before a call's name can only close a statement header; `do`
joins the bare-call keywords. Go keeps its old reading. Before, such a
call skipped the bare-call guards and could link to another file's
same-named member, like a context interface's `setOpen` property.

Fixes issue 2585.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 05:08:42 +00:00
Eric MinishandClaude Opus 5.5 18248d191e docs(agents): describe the instructions-file block the installer writes again (#1962)
AGENTS.md said the installer no longer writes an instructions-file block,
but since PR #704 the Claude, Codex, Gemini and opencode targets write a
short one again, while Cursor and Kiro only strip an old one. The
architecture notes and house rules now describe that block and keep tool
guidance in server-instructions.ts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 04:23:19 +00:00
Eric MinishandClaude Opus 5.5 fc470886ac fix(agent-eval): accept the trust dialog on "Yes", never type into a bare shell (#1925)
Current Claude Code builds list the per-folder trust dialog's options in
a different order, so itrun.sh now moves onto "Yes, I trust this folder"
before pressing Enter, whichever order the build uses, and stops instead
of typing the prompt into a bare shell when claude has already exited.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 04:22:24 +00:00
Colby MchenryandClaude Opus 5.5 5530676f41 fix(rust): a Self:: call is the function of the type whose impl it is in (#2700)
A Rust Self::x(...) call reached the resolver as a method call on a
receiver named Self, which names no type. So it fell to a guess by the
name alone, and linked only when nothing else in the project shared
that name. clap's Self::new(...) calls in impl Error, ValueParser and
the like stayed unresolved, and so did tokio's, axum's and serde's.
dioxus's Self::current() stopped resolving once its Android tooling's
AndroidTools::current was indexed.

Self is now the type of the impl (or trait) the call is written in. The
call reaches that type's function, or its variant for Self::Text(...),
in the type's own file first, then the nearest. As in Rust, the type's
inherent function comes first, with cfg twins standing for one another.
A trait impl's function is taken only when it is the only one, and
several trait impls of one name (From<A>, From<B>) are left unresolved.
Inside a trait impl, a call to the function's own name hands on to the
type's own function, so it never links to itself there. In the type's
own impl such a call is a recursion and stays one. A trait's own method
is the fallback only where nothing could shadow it.

A type from outside the project, such as axum's impl Listener for
tokio::net::TcpListener, has only the impls written beside the call.
When the type has no such function, the call stays unresolved instead
of going to another type's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 02:08:18 +00:00
Colby MchenryandClaude Opus 5.5 204315274e fix(cpp): every name a declaration lists has its type, like strings_found in ByteBuffer symbols_found, strings_found; (#2698)
C++ receiver inference read a declaration with a regex that wants the type
right before the name. A declaration that lists several names writes the type
once, before the first, so the later names were never read: google/breakpad's
`strings_found.Size()` after `ByteBuffer symbols_found, strings_found;`
linked nothing, and a later name could take the type of an earlier variable
of the same name further up.

matchCppListedDeclarator reads such a declaration from its start (specifiers,
the type with its template arguments paired, then each declarator with its
own pointer and reference marks, qualifiers, annotations, array bounds and
initializer) and gives a later name the declaration's type plus its own `*`s
and `&`s: `Foo *a, b;` declares one pointer. Names are told apart only by
commas at the statement's own depth, so template arguments, a call's or a
constructor's arguments, an initializer list or an expression list no name.
It runs only where the declarator regex finds nothing on a line, in all three
declaration readers: the receiver scan (and its header scan), a class's data
members (`Table *index_, *data_;`), and the check that a bare call names a
local.

A/B on main 036832d3 with current upstream clones: breakpad +30 added,
5 overload retargets wrong to right, 66 guesses now typed; protobuf +97,
1 retarget wrong to right, 9 removed (5 wrong, 4 single-candidate guesses on
message classes the C++ index lacks, now like the first name in their list);
rocksdb +43, 5 wrong removed; leveldb +4. glog and redis byte-identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 01:52:07 +00:00
Colby MchenryandClaude Opus 5.5 ed8957710f fix(objc): a file-scope constant or global is indexed, in an @implementation too (#2699)
Objective-C read a file-scope declaration through the generic variable
fallback, which takes only a declarator that is a bare name (`int count;`).
Its most common globals were never indexed: `NSString *const
SDWebImageErrorDomain = @"…";`, `static const NSInteger kMaxRetries = 3;`,
`static dispatch_queue_t queues[4];`. A declaration written inside an
@implementation, file-scope C in the language, was not read at all,
because the class counts as an enclosing scope.

- tree-sitter.ts: Objective-C takes C's declarator branch in
  extractVariable (initialized, pointer and array declarators, with the
  initializer as the signature), keeping the bare names it always read.
  A declaration whose parent is an implementation_definition takes the
  variable branch; its globals are qualified under the class, as the
  C functions written there already are. A block or function-pointer
  variable reads as a prototype, as in C, and stays out.
- c-cpp.ts: isConstDeclaration, C's isConst, is shared, and objc.ts uses
  it, so `static const NSInteger kMaxRetries` is a constant.
- name-matcher.ts: a global that a .m or .mm file defines joins the C
  types that #2690 made file-local (OBJC_FILE_LOCAL_KINDS). A new
  isObjcFileLocalElsewhere drops such a candidate in the exact-match and
  fuzzy chains, before one is picked, and isVisibleAcrossFiles applies it
  to the pick. Before the pick, so that a header's `extern NSString
  *MJConfigFile;` stands for the definition in variables.m. Rejecting only
  the pick left Hammerspoon's MJConfigFile references unresolved. Without
  the rule, React Native's completionHandler(nil, image) block-parameter
  calls reached a completionHandler in another .mm, and #include <vector>
  a .mm's vector.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 01:43:14 +00:00
Colby MchenryandClaude Opus 5.5 60b2f1e7bb fix(rust): a call reaches a project impl on a std type only as Rust would pick it (#2697)
A crate can't declare a method on String, Vec, Arc or NonNull, only
implement a trait for one, and name matching took those impls for any
call of that name. Every String::from("...") in dioxus's CLI went to its
impl From<IDI> for String, tokio's NonNull::from(waiter) calls to its
impl From<&Handle> for NonNull<Entry>, its tests' files.iter() to an
internal Storage trait's impl for Vec, and clap's String::from calls
to its impl From<Str> for OsString.

A call into a method of a project impl on a std type now stands only
when Rust could pick it. A std trait (From, PartialEq, ...) is
implemented for the crate's own type argument, so the call must pass
one: built from it (IDI::Install), self in an impl of it, or a name
the enclosing function declares with it (fn from(name: Str)). A trait
of the project's must be in scope in the calling file: declared,
implemented or imported there, or brought in by a glob that may carry
it. Otherwise the call is left unresolved, since what it calls is the
standard library's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 01:37:17 +00:00
Colby MchenryandClaude Opus 5.5 3273f20f47 fix(objc): a doc comment inside an @implementation documents the method below it (#2696)
Inside an Objective-C @implementation, tree-sitter-objc wraps each
method, function and declaration in an implementation_definition of its
own, and the comment above it is the wrapper's sibling, not the method's.
getPrecedingDocstring looked only at the method's own siblings, so no
method, function or static written in an @implementation had its doc
comment, while the same comment above an @interface property was read.

- tree-sitter-helpers.ts: implementation_definition joins
  DOCSTRING_WRAPPER_TYPES. Each wraps exactly one item, so the climb
  cannot hand a comment to a neighbour: a comment above an @synthesize
  stays its own.
- codegraph-kernel docstring.rs: the faithful port's wrapper list gains
  it too. The kernel does not walk Objective-C, so the entry only keeps
  the two lists alike.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 01:16:23 +00:00
Colby MchenryandClaude Opus 5.5 036832d3a7 fix(extraction): a build folder inside src/ is indexed for its Rust files too (#2695)
A folder named build below src/ was indexed for its TypeScript, JSX,
Vue, Svelte, Astro, Go and C# files, but not Rust's. dioxus's CLI keeps
its whole build pipeline in packages/cli/src/build/, gleam's compiler
its project and package compilers in compiler-core/src/build/, and
tauri-plugin its build-script helpers in src/build/. Cargo writes its
output to target/, so .rs files in such a folder are hand-written, and
they are now indexed as well.

Rust waited on its name matching: indexing dioxus's src/build/patch.rs,
which declares a private type Result, moved 158 Result references off
the crate's own alias. A private item now stays in its own module, so
those references keep their target.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 01:07:22 +00:00
Colby MchenryandClaude Opus 5.5 6f8e6caad9 fix(c,cpp): a source implements the header beside it that it reaches through other headers (#2693)
upb's reflection sources include only their internal header:
upb/reflection/field_def.c includes upb/reflection/internal/field_def.h,
which reaches the upb/reflection/field_def.h it implements through def.h.
So the translation-unit rule of PR 2684 never read field_def.c as that
header's implementation, and 343 calls from protobuf's Python extension,
plus 88 from its Lua binding, still went to the copy of upb that the PHP
extension bundles in php-upb.c.

When a source includes no header named like it in its own folder, one
there that it includes through other headers counts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 01:00:25 +00:00
Colby MchenryandClaude Opus 5.5 1b12bf5b49 fix(objc,c,cpp): an enum declared with NS_ENUM, NS_OPTIONS or CF_ENUM is indexed (#2694)
Foundation's enum macros declare most enums in Objective-C and in the C
and C++ headers beside it: `typedef NS_ENUM(NSInteger, SDImageCacheType)
{ … };`. The parser reads the macro as a broken typedef, so the enum and
its values were never indexed (SDWebImage had no enum at all), and a junk
type alias named after the first value, or after `(NSInteger, Name)` in
C++, stood in their place.

- c-cpp.ts: rewriteFoundationEnumMacros rewrites the macro in place as
  `enum Name {`. The `typedef`, the rest of the macro's name, its
  parentheses and its first argument (the underlying type, or an
  NS_ERROR_ENUM's domain) become spaces, so every offset stays. It matches
  on the code with comments, literals and directives masked, so the
  macro's own `#define` is left alone. NS_ENUM, NS_OPTIONS,
  NS_CLOSED_ENUM, NS_ERROR_ENUM, CF_ENUM, CF_OPTIONS and CF_CLOSED_ENUM
  share the shape. The C and C++ preParse run it first.
- objc.ts: preParse runs it before the lone-macro blank.

The kernel takes C and C++ input from the hoisted preParse, so it needs no
change. On the five repos' 1,342 C/C++ files the kernel and wasm agree
byte for byte, and 17 more files now parse clean enough for the kernel to
take them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 00:59:54 +00:00