528 Commits

Author SHA1 Message Date
Andrey Kumanyaev 97e34b66c0 fix(cpp): index hxx headers 2026-08-22 12:13:03 +02:00
Andrew Kumanyaev 19a6fc66b9 Merge pull request #633 from zzet/feat/member-extraction
feat(parser): emit Dart and Swift member declarations with localize seat protection
2026-08-20 09:15:55 +02:00
Andrey Kumanyaev 02490300d0 fix(temporal): match repo-local env-helper names case-insensitively
The repo-local Temporal env-helper allow-list matched user-supplied
names with a raw, case-sensitive map lookup, while the built-in names
went through strings.EqualFold. A capitalisation mismatch between
.gortex/temporal-allowlist.yaml and the call site failed silently: the
dispatch fell back to the hidden "heuristic" tier when the callee
carried an "env" marker, and lost its env attribution entirely when it
did not. Both doc comments already described the intended behaviour —
case-insensitive matching over lower-cased keys — so only the code had
drifted.

Lower-case the keys where the map is built and lower-case the callee at
lookup, restoring parity with the built-in list. Cover the whole opt-in
chain (env gate, allow-list file, indexer options, Go extractor) with a
case-mismatch regression test, and document the feature: the gate and
the file shape were previously undocumented outside source comments.

Closes #524
2026-08-19 22:03:46 +02:00
pbednarcik 6fc3453db3 csharp: stamp method_type_params on ordinary methods too
Review coverage sweep over call shapes found a real gap: the
method_type_params stamp lived inside the extension-only branch, so an
explicit Pack<int>(5) could not split an ordinary generic/non-generic
overload pair — the type-arg filter saw two non-generic candidates and
declined, leaving declaration order to win. Hoist the stamp out of the
extension block; rides the same csharp@12 salt.

Also pins named arguments and out-var arguments as counted call shapes
(both already handled by csharpCallArgCount — argument nodes either
way), each targeting the second-declared overload so the pins fail
under declaration order.
2026-08-18 20:02:18 +02:00
pbednarcik 86bf0e2e84 csharp: consult arity for ordinary overload sets (#559)
Arity narrowing existed only inside the extension binder; every other C#
overload set bound to whichever declaration came first — at 0.95
ast_resolved when the receiver was typed. Both halves of the evidence
were already in the graph (edge arg_count, candidate param_count /
param_required / param_variadic); nothing consulted them.

- csharpNarrowMethodByApplicability / csharpMethodAcceptsArgCount: the
  extension window's [required, declared] range with ordinary-method
  semantics — no this-slot discount, and param_count == 0 is a real
  arity, not missing evidence. Extension candidates are exempt (their
  own binder adjudicates them). A filter that would empty a set keeps
  it: narrowing can turn a refusal into a bind, never a bind into a
  refusal.
- resolveMethodCall narrows rawCandidates + candidates up front, so the
  exact-type passes and every fallback below see only invocable
  overloads. Pass 2's uniqueness guard now also converges when arity
  makes an overload set unique across directories.
- resolveFunctionCall narrows after the extension routing, covering the
  same-file pick and the locality cascade.
- The extractor stamps arg_count / type_arg_count on receiverless calls
  too (the scope rules that bound them never consulted arity — the
  exact premise #559 retires). csharp salt 11 -> 12.

Tests drive zzet's measured repros through the real extractor +
resolver: both declaration orders for the typed receiver, the
receiverless same-file set, zero-arity, optional-parameter and
params-tail windows.
2026-08-18 19:52:54 +02:00
Andrey Kumanyaev c82f37f7f8 feat(swift): emit init, deinit, and subscript members in types and extensions
These declarations are their own grammar rules and matched no query, so the
members were absent and call edges made inside their bodies were dropped for
lack of an enclosing function. They now emit with the cross-language
constructor spelling, work identically inside extensions, and recover the
call attribution. A protocol init requirement stays unemitted.
2026-08-18 15:21:14 +02:00
Andrey Kumanyaev c2c91bbb82 feat(dart): emit class and mixin fields, enum constants, and bare setter names
A field declaration inside a class body was dropped entirely, enum bodies
emitted only the type, and setters were named 'set foo' so a search for the
property name could never match. Fields now emit one node per bound name
with the member conventions the other languages use, enum constants follow
the shared Enum.constant identity, and accessors carry the bare property
name with an accessor stamp for disambiguation.
2026-08-18 15:21:14 +02:00
Andrey Kumanyaev 7133677d62 Merge remote-tracking branch 'origin/main' into feat/localization-close-grep-gap
* origin/main: (142 commits)
  Exempt Python dunders from dead-code analysis
  store_sqlite: normalize native separators in generated dir columns
  store_sqlite: space WAL auto-checkpoints at 8k pages for index-write bursts
  store_sqlite: three-phase seeded reindex benchmark
  Update internal/semantic/tstypes/fact_spool.go
  ci(deps): bump github/codeql-action/upload-sarif
  store_sqlite: guard the json transport against unmarshalable payloads
  resolver: scope page_load to the pass clock
  resolver: split commit_apply interior timings
  store_sqlite: bind resolved-conversion updates as one json_each relation
  resolver: attribute pass interior time and churn shapes
  tstypes: true up spool capacity hints and page-cap/deadline comments
  resolver: note razor marker removals; make retention counters mean what they log
  fix(indexer): use incremental contract cleanup on untrack
  fix(mcp): cache index health scans
  fix(agents): register the OpenCode MCP server at user scope
  feat(agents): install the Gortex sub-agents for Codex CLI
  style: gofmt the init wizard test
  test(agents): regenerate the render goldens for the widened fence
  fix(uninstall): remove Claude Code's per-community skills too
  ...

# Conflicts:
#	docs/agents.md
#	internal/mcp/overlay_view.go
2026-08-17 20:36:56 +02:00
Andrew Kumanyaev f529e24cf1 Merge pull request #526 from zzet/chore/drop-code-orphaned-by-sqlite-only
Drop the code orphaned by the SQLite-only backend transition
2026-08-14 17:34:59 +02:00
Andrey Kumanyaev 3fd504791d feat(indexer): isolate parser config and lifecycle 2026-08-14 11:06:23 +02:00
Tien Dung Dao f8984f8320 Merge remote-tracking branch 'upstream/main' into fix/csharp-params-shape 2026-08-14 14:51:30 +07:00
Andrey Kumanyaev bd92b63c74 chore(parser): drop the unreferenced parser helpers
NodeText, WrapNode and WrapTree had no callers left: extractors slice
src[start:end] directly and the shim Node/Tree values are constructed
inside tsitter itself. The per-language complexity wrappers and the
Temporal env-helper configurator were likewise unreachable, so the
stranded EnvHelperConfigurable interface goes with them.
2026-08-14 09:49:33 +02:00
Andrey Kumanyaev d048a98f29 fix(parser): bound subprocess extractor wire output 2026-08-14 08:56:24 +02:00
Tien Dung Dao 6f09faa425 test(csharp): pin params overload extraction 2026-08-13 15:20:22 +07:00
Andrey Kumanyaev d698f0be1e fix(cpp): extract out-of-line member definitions
A definition written outside its declaring scope — `void ns::Cls::run() {}`
in the .cpp for a header's class — declares itself with a
qualified_identifier, a shape no function_definition pattern admitted. The
definition emitted no node at all, and every call in its body went with it:
a deferred call needs an enclosing function range to attach to. In a
translation unit whose class lives in a header, that is the whole file.

Admit a qualified declarator in each function_definition pattern and read
the qualifier structurally. The trailing segment decides the shape: a type
owner yields a method keyed on the owner (member_of the class when this
file declares it, so `A::run` and `B::run` no longer collide), and an
all-namespace qualifier yields a free function carrying the qualifier as
scope_ns. Destructors, operators, pointer and reference returns, and
out-of-line template members reach the same walk.

Folds cppQualifiedCallName and lastIdentifier into that one walk — a
qualified callee and a qualified declarator are the same shape. lastIdentifier
scanned direct children only, so once a name nested it returned the first
segment rather than the last (`ns::Cls::bar` gave `ns`).
2026-08-13 09:32:56 +02:00
Tien Dung Dao 9b78962c20 fix(cpp): emit nested qualified call edges 2026-08-13 12:39:07 +07:00
Tien Dung Dao bc3c68f734 fix(csharp): recover params parameter shape 2026-08-13 11:24:01 +07:00
Andrey Kumanyaev 7513af9019 fix: stop two call shapes binding to the wrong target
Both defects shipped in the generic-call work and both turn a previously
missing edge into a confidently wrong one, which is the worse failure.

C#: a static-form extension call passes the receiver as its first argument,
so the arity window must not discount the `this` slot. That distinction
rides on the receiver's spelling, which the extractor stamped only after
the local / parameter / builtin lookups missed — and a namespace-qualified
receiver takes the chain-walker branch before reaching it, so it arrived
carrying no spelling at all. The binder read `Lib.BagExt.Add(bag)` as
extension form, subtracted a slot the argument list had already filled, and
bound the two-parameter overload; `BagExt.Add(bag)`, the same call written
unqualified, bound the correct one. A qualified receiver the chain walker
could not type now carries the same evidence, and the comparison is against
the trailing segment so both spellings agree.

Go: the generic-call patterns match `Zero[int]()`, but Go spells indexing a
func-valued map or struct field identically. The safety argument for that
was that a package-scope name is a func, a var or a type and never two —
which is true, and covers neither a func-valued STRUCT FIELD (it lives in
the type's namespace, free to share a name with another type's method) nor
a LOCAL shadowing a package function. Both were bound: `t.handlers[k]()`
landed on an unrelated `Other.handlers`, and a shadowed `Handler[k]()`
landed on the package's `Handler` at the ast_resolved tier.

The fix is the property the impostors cannot fake. A real instantiation can
only target a declaration that DECLARES type parameters, so the extractor
marks the call shapes it could not disambiguate and the resolver binds them
only to a generic declaration. That runs at the shared candidate lookup, so
every name tier inherits it, and it narrows only — an unmarked edge is
untouched and an empty result leaves the call unresolved, which is the right
answer for an index or a conversion.

Both extractor versions are bumped: existing indexes carry the wrong edges
and only a re-extraction removes them.
2026-08-12 09:54:11 +02:00
Andrew Kumanyaev 756aa74d38 Merge branch 'main' into fix/generic-call-edges-scala-go-cpp-swift 2026-08-11 18:11:28 +02:00
Andrey Kumanyaev a061b73c51 fix(swift): emit call edges for generic calls and ordinary member calls
Two losses in the same dispatch, the second far larger than the first.

Adding type arguments reroutes ANY Swift call away from `call_expression`:
`lowerGeneric<Int>()`, `obj.doThing<Int>()` and `MyType<Int>(x: 1)` all parse
as a `constructor_expression` whose constructed type swallows the whole
dotted path. Nothing in the query matched one, so every generic call — free
function, member, or initializer — emitted nothing. The constructed type's
own `type_identifier` children give the path back; the ones nested in a
`type_arguments` list are the arguments and are excluded.

The member branch was then gated to factory chains only, on the stated belief
that the bare-identifier pattern stayed "authoritative for ordinary
obj.method()". It never was: that pattern needs a `simple_identifier` as a
DIRECT child of the call, and a member call nests it under a
`navigation_expression`. So every plain `obj.method()` and `self.method()`
emitted no edge either — Swift was recording free calls and factory chains
and nothing in between. Removing the gate restores what every other language
already does.
2026-08-11 10:08:41 +02:00
Andrey Kumanyaev d0ebbf990c fix(cpp): emit call edges for templated and namespace-qualified calls
The C++ call query matched two callee shapes — a bare `identifier` and a
`field_expression` with a `field_identifier` — and C++ has five. The three it
missed emitted no call edge at all:

  generic<int>()                 -> function: (template_function …)
  obj.m<int>() / ptr->m<int>()   -> field: (template_method …)
  ns::helper() / std::move(x)    -> function: (qualified_identifier …)

The last one is not about templates and is the larger of the two losses:
every namespace-qualified free call in the language was dropped, so a
codebase that qualifies consistently recorded almost no free calls at all.
Rust already carries the equivalent `scoped_identifier` pattern; C++ now
matches it, binding on the trailing name the call actually names.
2026-08-11 10:08:41 +02:00
Andrey Kumanyaev 597d257004 fix(go): emit call edges for explicitly instantiated generic calls
The call patterns pinned `function:` to `(identifier)` / `(selector_expression)`,
and a Go call spelling ONE type argument matches neither — through two
different mechanisms, so fixing only the obvious one leaves half the calls
missing:

  Zero[int]()      obj.M[int]()      -> function: (index_expression …)
  OneArg[int](1)   obj.N[int](1)     -> type_conversion_expression, not a
                                        call_expression at all

Two or more type arguments were never affected: `F[string, int](a, b)` keeps
a plain callee with a sibling `type_arguments` field, which is why the gap
looked like it did not exist.

Both shapes are genuinely ambiguous in the grammar — an index_expression
callee is also how `handlers[key]()` parses, and a conversion to a generic
type is spelled exactly like a one-argument generic call. Widening the
patterns therefore emits an unresolved stub for those too. That is safe for a
reason specific to Go rather than luck: a package-scope name is a func, a var
or a type and never two of them, so a stub named after a variable or a type
finds no function candidate and stays unresolved — and an unresolved target
is a placeholder that reverse walks ignore. The resolver test asserts exactly
that, alongside a real cross-file generic call that does resolve.
2026-08-11 10:08:41 +02:00
Andrey Kumanyaev 5df55e97b7 fix(scala): emit call edges for calls with explicit type arguments
extractCall switched on the callee node and handled only `identifier` and
`field_expression`, falling through to `default: return` for anything else.
tree-sitter-scala wraps any callee carrying explicit type arguments in a
`generic_function` node, so `f[T]()`, `obj.m[T](x)` and `Future[Int] { … }`
hit that default and emitted nothing at all — no edge, not even an
unresolved stub. `query callers` on such a method then answers
`total_edges: 0` with the `likely_unused` caveat, making a resolution
failure indistinguishable from dead code.

Unwrapping `generic_function` to its real callee lets the two existing cases
apply unchanged, so a generic call is recorded exactly like its plain
spelling — which is what the test asserts, pairing each shape with and
without type arguments.
2026-08-11 10:03:23 +02:00
Andrey Kumanyaev 95e596f113 fix(csharp): count the receiver argument when an extension is called statically
Applicability compared argument counts against the EXTENSION-form window,
where the receiver fills the `this` slot. An extension invoked through its
declaring class (`BagExt.Add(bag)`) passes that receiver as the first
argument instead, so every parameter has to be covered by the argument list.
Reading one form as the other shifts the window by one, which does not
merely lose a bind: with a sibling overload present it leaves the WRONG
candidate as the sole survivor, and narrowing then binds it.

The resolver had no way to tell the forms apart, so the extractor now stamps
a bare receiver's spelling — but only in the one case nothing else explains
it, after the local, parameter and builtin lookups have all missed. That
restriction is what makes the signal trustworthy: reaching it means no value
in scope carries the name, so a receiver equal to a candidate's declaring
class is a type reference rather than a variable shadowing it. Receivers the
extractor did type are values by construction and carry no such stamp.

Beyond removing the misbinding this also resolves static-form calls
correctly, which previously stayed unresolved on any overloaded set.
2026-08-11 09:42:19 +02:00
Andrey Kumanyaev fa04773c71 fix(parser): stop reading a generic declaration's own name as a function value
The function-as-value capture treats a `(` after an identifier as proof the
identifier is a callee, and leans on that to skip a declaration's own name.
A generic declaration puts the type parameters in between —
`AddFooDecorator<TInterface, TImpl>(` — so the `(` is not where the rule
looks and the method's own name was captured as a function reference. The
placeholder edge carried the DECLARATION line, and the gate then bound it to
whichever same-name sibling it found first, minting an edge between two
overloads anchored at a line where no call exists. Reported as the second
symptom of issue #534.

Same root cause as the dropped call edges — an identifier followed by `<` —
but the byte rule cannot simply learn `<`: in JavaScript, TypeScript and
every C-family language the capture serves, `f < g` is a comparison. The
tree answers exactly and for all of them at once: a node that fills its
parent's `name` field where that parent also declares `parameters` is a
declaration however it is spelled.
2026-08-11 09:35:01 +02:00
Andrey Kumanyaev d6ac9f8e84 fix(csharp): bind same-name extension overloads by call applicability
Once generic invocations emit edges, the reporter's fixture still resolved
to nothing. Both `AddFooDecorator` overloads extend `IServiceCollection` and
both live in `Lib.Extensions`, so neither the receiver-type tiers nor the
namespace-visibility tie-break could separate them, and the binder's
ambiguity refusal dropped all three call sites. Overloaded extension methods
over one interface are the standard .NET DI registration shape, so this hit
far more than the reported fixture.

C# separates such a set by APPLICABILITY, which is exact rather than
heuristic: an overload is a candidate only if the call's argument count fits
its parameter list and its type-parameter count matches any explicitly
spelled type arguments. Neither fact was in the graph, so both are now
recorded — argument and type-argument counts on the member-call edge, and
`param_count` / `param_required` / `param_variadic` on the method node.
Node-level arity rather than the existing KindParam nodes because those
carry no default-value marker and cannot answer "how many MUST be supplied".

Applicability runs before the visibility tie-break, matching §12.8.10.3
(each scope level considers only applicable candidates). It narrows only:
missing evidence, an unreadable stamp, or a filter that would empty the set
all leave the candidates untouched, so this can turn a refusal into a bind
but never a bind into a different one. Where it does narrow, the winner must
still clear the visibility veto — the pool-unique rule's waiver is scoped to
sets applicability never touched, so an overload the call site cannot see is
refused rather than promoted by arity.

Two traps the fixtures pin. The vendored grammar does not resolve a `params`
entry into a parameter node, so a naive count claims one parameter for a
two-parameter method; a list that cannot be read in full now yields no
evidence at all, leaving the method universally applicable instead of
wrongly excluded. And a defaulted parameter has no equals-value wrapper —
only a bare `=` token — so optional parameters were invisible.

Also fixes parameter positions: a discard (`Foo(int _, string name)`) emits
no node but still occupies its slot, and skipping the slot with the node
renumbered every parameter after it.
2026-08-11 09:34:52 +02:00
Andrey Kumanyaev d165ab95af fix(csharp): emit call edges for invocations with explicit type arguments
The C# call-site query pinned every invoked name to an `identifier`.
A call that spells its type arguments — `services.AddFooDecorator<I, T>()`,
`GetRequiredService<T>()`, `AddSingleton<TI, TImpl>()` — parses that name as
a `generic_name` instead, so it matched no pattern at all and produced no
call edge: not an unresolved stub, nothing. The method-access patterns had
the same constraint, so the call earned no reference edge either.

The result was that a generic method could be called from everywhere and
still answer `query callers` with `total_edges: 0` and the `likely_unused`
caveat — a resolution failure indistinguishable from dead code. Generic
invocation is the dominant shape in .NET, so this covered a large fraction
of every indexed C# repo. Rust hit this same class of bug (its query carries
a note about 5,301 lost turbofish sites); the C# query is now equally
explicit about it.

Each affected pattern gains an alternation that accepts either spelling and
captures the inner identifier, so the callee name stays bare and every
downstream tier is unchanged. The extractor version is bumped so
already-indexed files re-extract instead of keeping the edge-less graph, and
Razor templates are salted with C# since they are extracted by it.

The test pairs each call shape with and without type arguments and asserts
they behave identically, which is the property that was actually violated.
2026-08-11 09:33:38 +02:00
Andrew Kumanyaev 603d43bc50 Merge pull request #495 from zzet/fix/gdscript-static-autoload-connect-resolution 2026-08-07 23:35:49 +02:00
Andrey Kumanyaev e53ad3684d Resolve GDScript calls the receiver names, and refuse the ones it doesn't
Four attribution gaps survived receiver-aware call extraction. Measured on
a Godot project of ~490 files, 175 of 1002 targets with callers missed at
least one calling file.

Static methods and autoload callers. Neither was a distinct defect — the
minimal shapes from the report already resolved. What did not:

  - A call outside every func was dropped entirely. `var hp = Stats.roll()`
    and `@onready var ui = Hud.build()` are a script's whole initialisation
    layer, and Godot puts a lot there. Attribute them to the file, the same
    rule Python applies to module-level calls.
  - `self.tick()` in a script with no `class_name` — which every autoload
    entry point is — has no nameable receiver, so the member shape carried
    no evidence and the call settled on the name-only tier find_usages
    suppresses by default. The file declares the callee, so emit the
    free-function shape and let the same-file tier bind it exactly.
  - `const Persist = preload("res://…")` is how a static-utility script
    with no `class_name` is reached, and the alias is file-local: the same
    name one directory over means a different script. Record the loaded
    path on the const and bind through it.

Same-named methods cross-linking. The dangerous one: a call states its
receiver, and when no member of that receiver was found a weak tier still
answered with whatever same-named method sat nearest the caller. With
`apply` in several classes, `Carry.apply`'s caller list held the calls
belonging to `Items` and `Effects` — the count looks reassuring while
pointing at code the change cannot reach. A receiver gate now demotes a
provable mismatch to the speculative tier, mirroring the C# gate. Only a
provable one: inherited dispatch survives by walking the receiver's
declared base chain, and a base that cannot be read as one in-repo class
name, an autoload or alias receiver on its own script, and a receiver the
calling file declares itself all decline to judge.

Signal wiring. Godot passes the method itself — `pressed.connect(_on_x)`,
`Callable(self, "_on_x")`, and the scene's own `[connection … method=…]`
block. None is a call, so rename rewrote the definition and the ordinary
call sites and left the wiring pointing at a name that no longer exists:
the project still parses, nothing errors anywhere, and the button simply
stops working. All three shapes are now usages, so rename rewrites them —
including in the `.tscn`, bound through the target node's script rather
than by name, which is right only while the handler name is unique and
`_on_pressed` never is.

The receiver gate runs whole-graph, so it is gated on the resolve having
seen a GDScript node at all; nothing else it reads can move a verdict.
2026-08-07 23:02:16 +02:00
Peter Bednarcik 939fe6a856 csharp: emit interface base-list extends edges
An interface declaration's base list never reached emitCSharpBaseList,
so a derived interface had no hierarchy edge in the base graph:
inherited members could not resolve through it and dispatch families
never spanned interface inheritance. Every base of an interface is an
interface and the relation is inheritance, so each entry emits
EdgeExtends, bypassing the class-path discrimination.
2026-08-07 17:28:16 +02:00
Andrew Kumanyaev 32f8bf3c7d Merge branch 'main' into fix/csharp-member-access 2026-08-07 00:21:08 +02:00
Peter Bednarcik a178ee0ab3 csharp: emit property nodes for record positional parameters 2026-08-06 19:53:56 +02:00
Andrew Kumanyaev 58535a6899 Merge pull request #478 from pbednarcik/fix/csharp-record-members
csharp: extract record members
2026-08-06 18:43:35 +02:00
Peter Bednarcik 36cb521698 csharp: chained reads on param receivers survive the noise rule 2026-08-06 18:38:13 +02:00
Andrew Kumanyaev 43d5a11f1c Merge pull request #476 from zzet/feat/localization-evidence-depth
Deepen localization evidence pages and extract missing symbol classes
2026-08-06 18:25:52 +02:00
Peter Bednarcik 3802ac2213 csharp: emit member-access read/write edges 2026-08-06 17:58:01 +02:00
Andrey Kumanyaev 0422f27dc9 Detect a C++ header's language from its content, not its extension
The bulk index walk resolves every file's language before reading any of
them, then reuses that answer to pick the extractor. For a contested
extension the walk can only report the extension's default, so `.h`
always resolved to C: every header-only C++ library was parsed by the C
extractor, which cannot see past `template <…>` and dropped the file's
free template functions, class members and namespaces.

Re-detect at parse time for the extensions whose default is a guess —
the bytes are already in hand there, so it costs no extra read and gets
the full content rather than a bounded prefix. sniffAmbiguous is now
gated on the same set, so an uncontested extension can never be
re-typed from content.
2026-08-06 14:55:13 +02:00
Peter Bednarcik 57fe03ceb4 csharp: extract record members 2026-08-06 14:23:01 +02:00
Andrey Kumanyaev 4585d650ac Extract free template function definitions in C++ headers
A free template function is a function_definition wrapped in a
template_declaration, and the extractor only ever matched the inner
definition. Its symbol therefore started below the `template <…>`
header — a line-anchored lookup on the header found nothing — and an
explicit specialization, whose declarator is `name<Args>` rather than a
bare identifier, produced no symbol at all.

Template-wrapped definitions at file or namespace scope now emit from
the template_declaration, so the span covers the header, and the
declarator name is resolved through the specialization form as well.
Members declared inside a class or struct body are not free functions
and keep their existing path unchanged.
2026-08-06 11:24:03 +02:00
Andrey Kumanyaev 9d381eea09 Extract named function expressions in JavaScript and TypeScript
A named function expression carries a name the grammar exposes on the
expression itself, but only function_declaration was matched, so
`const x = function foo() {}`, `module.exports = function foo() {}`,
`{ key: function foo() {} }` and `export default flag && function foo() {}`
produced no symbol at all. Both extractors now match the named
expression form and emit it through the same path as a declaration
(same kind, spans, signature and file-defines edge). Anonymous
function expressions have no name field and stay unemitted.

The JavaScript and TypeScript extractors keep separate queries and
walkers, so the pattern and the shared emit helper are added to each;
the TypeScript one covers the TSX grammar as well.
2026-08-06 11:12:44 +02:00
Andrey Kumanyaev b5ac4571d1 Stamp receiver types on capitalized Dart call chains
A Dart call whose chain head is a Capitalized identifier —
`TiledAtlas.fromTiledMap(1)` — names the type the callee lives on, so
the call edge now carries that identifier as receiver_type. The
resolver already prefers a receiver-typed candidate, so such a call
binds to the member on that exact type instead of any same-named symbol
elsewhere in the graph.

Scope is deliberately narrow: only a two-segment chain qualifies (in
`Foo.bar.baz()` the head types `bar`, not `baz`), lowercase heads are
left alone rather than guessed at through local-variable inference, and
an import-alias head keeps its extern attribution unstamped because a
library prefix is not a type.
2026-08-06 11:00:38 +02:00
Andrey Kumanyaev 7a5d4978a4 Extract Dart named, factory, const, and redirecting constructors
Every dotted Dart constructor now emits a member symbol the way an
ordinary method does: KindMethod, bare constructor name, owner-qualified
id, an EdgeMemberOf to the enclosing type, and a span that reaches
through the body when there is one.

Two grammar shapes were being missed. A body-less constructor (named,
const, redirecting factory) hangs off a plain `declaration` rather than
`method_signature`, so the class-body walk never saw it; the walk now
visits both wrappers in the same pass. And a constructor signature's
`name` field points at the leading TYPE identifier, so name extraction
read `TiledAtlas` instead of `fromTiledMap` and the unnamed-constructor
guard dropped the node; the name is now the identifier after the dot.

Unnamed constructors stay unemitted — `Foo()` constructs the class and
must not be shadowed by a phantom member.
2026-08-06 10:55:43 +02:00
Peter Bednarcik df5952ea5a csharp: appease staticcheck QF1002 in awaited-call helper 2026-08-06 09:21:46 +02:00
Peter Bednarcik cc0ac1d220 csharp: extract this?./base?. conditional-access calls 2026-08-06 08:45:00 +02:00
Peter Bednarcik 41df80e757 csharp: anchor awaited-call typing to the enclosing scope 2026-08-05 23:01:45 +02:00
Peter Bednarcik 39e47609ae csharp: type awaited receivers by unwrapping Task<T> 2026-08-05 23:01:45 +02:00
Andrew Kumanyaev 0d6fd76e94 Merge pull request #470 from zzet/fix/surface-unresolved-calls-tscn-and-dockerfile-indexing 2026-08-05 19:08:54 +02:00
Andrew Kumanyaev a27e61db93 Merge pull request #466 from pbednarcik/fix/csharp-this-base-calls
csharp: extract this./base.-qualified member calls
2026-08-05 18:28:42 +02:00
Andrey Kumanyaev 9e54a08265 feat(godot): link scenes to the scripts and sub-scenes they mount
`.tscn` / `.tres` / `project.godot` were claimed by the generic
signature-only forest grammar, which emits no edges, so
`[ext_resource type="Script" path="res://…"]` produced nothing. In Godot
a script is normally reached through the scene that instantiates it, so
a script's blast radius stopped at its own callers and missed every
scene that mounts it.

A hand-written extractor now reads all three formats: scene nodes become
symbols keyed by their NodePath, `[ext_resource]` and `[autoload]`
entries become references, and a `script =` / `instance=ExtResource(…)`
assignment attributes to the scene node carrying it. Both Godot 4
(quoted string ids) and Godot 3 (bare integer ids) dialects parse.

A new resolver pass binds those `res://` references — and GDScript's
`preload("res://…")`, which never resolved either — onto the indexed
file, matching by path suffix because `res://` is rooted at the Godot
project rather than the repository, and refusing when two projects in a
workspace make the path ambiguous.
2026-08-05 18:26:59 +02:00
Andrey Kumanyaev 5e352f7b40 fix(parser): index Dockerfile.<suffix> build files
Detection matched only the exact `Dockerfile` / `Containerfile`
basename and the `.dockerfile` extension, so the common per-purpose
convention — `Dockerfile.perf`, `Dockerfile.ci`, `Containerfile.dev` —
fell through every lookup and was never indexed at all.

Registry entries now accept a third form, `<Stem>.*`, matched against a
basename of that stem with any suffix. It is consulted after the
extension lookups, so a suffix that is itself a registered extension
still wins and `Dockerfile.md` stays markdown.
2026-08-05 18:23:55 +02:00