Skip to content

Commit c12bb60

Browse files
committed
fix(fsharp): restore dual function_type/curried_spec detection for val (#1162)
The npm and cargo tree-sitter-fsharp 0.3.0 grammars — though sharing a version tag — still emit type signatures with different node shapes: WASM 0.3.0 produces `function_type` directly under `value_definition`, while cargo 0.3.0 wraps every signature in `curried_spec` with `arguments_spec` children for function types. #1165 removed the `function_type` branch on the assumption that both grammars had converged at v0.3.0, which broke WASM extraction: every `val name : a -> b` declaration was being indexed as a `variable` instead of a `function`. Restore the dual-shape detection in the TypeScript extractor and update the documentation accordingly. Also clarifies the nested-module test comment in fsharp-signature.test to reflect that the WASM signature grammar is now at v0.3.0 but still emits ERROR nodes for `module Foo = ...` (the fix is still pending, tracked under #1161).
1 parent 02cd2f1 commit c12bb60

3 files changed

Lines changed: 37 additions & 19 deletions

File tree

crates/codegraph-core/src/extractors/fsharp.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ fn match_fsharp_node(node: &Node, source: &[u8], symbols: &mut FileSymbols, _dep
3636
///
3737
/// Source files use `named_module` for the top-level `module M = …` and
3838
/// the signature grammar (cargo 0.3.0) wraps nested signature modules in
39-
/// `module_defn` nodes. WASM signature grammar (npm 0.1.0) currently
40-
/// produces ERROR nodes for nested signature modules so we cannot recover
41-
/// qualification there — tracked under #1161.
39+
/// `module_defn` nodes. The WASM signature grammar currently emits ERROR
40+
/// nodes for nested signature modules so we cannot recover qualification
41+
/// there — tracked under #1161.
4242
fn enclosing_module_name(node: &Node, source: &[u8]) -> Option<String> {
4343
let mut parts: Vec<String> = Vec::new();
4444
let mut current = node.parent();

src/extractors/fsharp.ts

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -317,17 +317,34 @@ function handleValueDefinition(
317317
findChild(pattern, 'identifier');
318318
if (!ident) return;
319319

320-
// The grammar wraps every type signature in `curried_spec`. A function type
321-
// (e.g. `val add : int -> int -> int`) contains one or more `arguments_spec`
322-
// children; a plain value (e.g. `val pi : float`) wraps a single `simple_type`.
323-
const curriedSpec = findChild(node, 'curried_spec');
320+
// The npm and cargo tree-sitter-fsharp 0.3.0 grammars — though sharing a
321+
// version tag — emit type signatures with different node shapes:
322+
// • WASM (npm 0.3.0 ionide tarball): `function_type` is the explicit
323+
// function-type kind, present as a direct child of `value_definition`
324+
// for `a -> b` types; plain values (e.g. `val pi : float`) appear as
325+
// `simple_type`.
326+
// • Native (cargo 0.3.0): every type signature is wrapped in
327+
// `curried_spec`. A function type contains one or more `arguments_spec`
328+
// children; a plain value wraps a single `simple_type`.
329+
// Classify as a function whenever `function_type` appears OR a
330+
// `curried_spec` contains an `arguments_spec` child, so both engines stay
331+
// in parity until the grammars converge.
324332
let hasFunctionType = false;
325-
if (curriedSpec) {
326-
for (let i = 0; i < curriedSpec.childCount; i++) {
327-
if (curriedSpec.child(i)?.type === 'arguments_spec') {
328-
hasFunctionType = true;
329-
break;
333+
for (let i = 0; i < node.childCount; i++) {
334+
const c = node.child(i);
335+
if (!c) continue;
336+
if (c.type === 'function_type') {
337+
hasFunctionType = true;
338+
break;
339+
}
340+
if (c.type === 'curried_spec') {
341+
for (let j = 0; j < c.childCount; j++) {
342+
if (c.child(j)?.type === 'arguments_spec') {
343+
hasFunctionType = true;
344+
break;
345+
}
330346
}
347+
if (hasFunctionType) break;
331348
}
332349
}
333350

tests/parsers/fsharp-signature.test.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,14 @@ describe('F# signature (.fsi) parser', () => {
4242
});
4343

4444
it('extracts val declarations nested inside a module signature', () => {
45-
// The WASM tree-sitter-fsharp 0.1.0 signature grammar does NOT produce a
46-
// `module_defn` for `module Foo = ...` — it emits ERROR nodes and the
47-
// `val` declarations float to the top level (so they're indexed as
48-
// `add`, not `Foo.add`). The cargo 0.3.0 grammar parses it correctly
49-
// and the Rust extractor qualifies as `Foo.add`. Grammar version skew
50-
// is tracked under #1161; once npm bumps to 0.3.0+ this test should
51-
// assert `Foo.add` to match the native engine.
45+
// The WASM tree-sitter-fsharp signature grammar (currently v0.3.0) does
46+
// NOT yet produce a `module_defn` for `module Foo = ...` — it emits
47+
// ERROR nodes and the `val` declarations float to the top level (so
48+
// they're indexed as `add`, not `Foo.add`). The cargo 0.3.0 grammar
49+
// parses it correctly and the Rust extractor qualifies as `Foo.add`.
50+
// The WASM grammar fix is tracked under #1161; once the signature
51+
// grammar emits `module_defn` for nested modules, this assertion
52+
// should be updated to expect `Foo.add` to match the native engine.
5253
const { symbols } = parseFSi(`module Foo =\n val add : int -> int\n`);
5354
expect(symbols.definitions).toContainEqual(
5455
expect.objectContaining({ name: 'add', kind: 'function' }),

0 commit comments

Comments
 (0)