mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-11-12 08:41:34 +00:00
* fix(deps): upgrade native tree-sitter to 0.25.1 * fix(tree-sitter): preserve sync loading and update verified capture baselines Route C# scope queries through the synchronous native grammar loader so the compiled analyze module remains require-compatible on Node 22.18. Refresh only the audited empty-Python-module and renamed-PHP-fixture captures, and pin the runtime assertion to 0.25.1. Validated 479 targeted tests, all 16 scope-capture benchmark cases, Python capture/import guards, compiled C# worker parsing, build, typecheck and formatting. Performance budgets are unchanged. * fix(tree-sitter): refresh upstream vendors and preserve parser semantics * fix(ci): refresh prebuilds and support Windows ARM Refresh macOS, Linux ARM, and Windows x64 grammar binaries from the successful native CI builds. Pin node-gyp 12.4.0 so the Windows ARM jobs recognize Visual Studio 2026. Use local Git filtering for E2E to avoid PR-files API timeouts on generated parser diffs. Validation: 143 focused tests, Actionlint, and all 16 scope-capture benchmark checks passed. Full npm test run started; native database suites still running. * fix(ci): refresh Windows ARM grammar prebuilds Import all seven Windows ARM grammar artifacts from successful native build run 37965761760. Artifact filenames and SHA-256 hashes verified; all 42 native build jobs passed. Validation: 31 prebuild coverage tests passed. * fix(bench): stabilize scope capture timing guard Warm each input size seven times and use 21 median samples while retaining isolated timing blocks. Yield between languages so native finalizers release allocations outside the measured sections. Keep all fixture sizes, fingerprints, scaling formulas, and budgets unchanged. Validation: all 16 language guards passed with a 2 GB heap limit (1.38 deep-chain ratio; 1518 MB peak RSS). Linear and quadratic controls passed their expected guard outcomes. Formatting and graph change analysis passed. Full npm test is running without failures so far. * fix(parser): reduce TypeScript capture traversal cost * fix(test): await parser worker readiness before teardown * fix(parser): load C# synchronously in workers * fix(grpc): resolve native proto detections against their own file With the native proto grammar active, sibling .proto files that declare the same short service name under different packages tied in the directory-based resolveProtoConflict heuristic and both providers were dropped. A proto-origin detection already knows its defining file, so use that file's entry before falling back to the heuristic. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * ci(packaging): assert native grammars load from the installed tarball The packaged-install smoke only ran `gitnexus --version`, so a grammar that failed to load from the published package (bundled runtime, patched peer manifests, vendored prebuilds, C# sync loader, Protobuf wrapper) would ship silently; the dev-tree ABI smoke also accepts vendored grammars that report unavailable. Parse a sample for every registered grammar plus the native Protobuf plugin from the installed package, on the main thread and in a worker thread. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * ci(tree-sitter): track compatibility workarounds instead of 0.25 readiness The daily readiness report still targeted tree-sitter@0.25.0, so after the upgrade it would keep reporting "Blocked" and never close its tracking issue. Repurpose it to track what we carry for the bundled runtime: derive the target from the package.json pin, list each pack-time peer patch from prepare-tree-sitter-bundle.cjs with its drop condition (bump, wait for an upstream release, transitive, or intentionally pinned), and reword the buckets. The workflow adopts the issue opened under the old title. Also update the grammar-update-monitor header and auto-PR text, which still described the 0.21.1 runtime and a tree-sitter-c hold. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(dart): assert both nested labels target the loop; fix Zig call-shape doc The nested-label CFG test only jumped to `outer` and checked reachability, which still passes when a jump is retargeted to the inner loop (its exit reaches the outer header). Jump to both labels and assert the exact `continue` edges; dropping the inner label now fails the test. Update the Zig callable-flow comment: call_expression now wraps actuals in a required `arguments:` field. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Codex <codex@openai.com> Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
446 lines
14 KiB
TypeScript
446 lines
14 KiB
TypeScript
/**
|
|
* Cross-language coverage for the leading-doc `descriptionExtractor`
|
|
* (issue #2270). Confirms every documentable language registers the hook, and
|
|
* exercises each doc-comment family through the real provider hooks:
|
|
* - block doc comments (double-star / bang): TypeScript, C++ (Java/Kotlin elsewhere)
|
|
* - triple-slash line runs: Rust, C#
|
|
* - godoc double-slash runs: Go
|
|
* - hash runs: Ruby
|
|
* - PHPDoc docblock fallback: PHP
|
|
*
|
|
* Dart and Swift share the default block / triple-slash config already exercised
|
|
* their native grammars can fail to load in some environments, so they are
|
|
* covered by the registration check (no parse) rather than a behavior parse.
|
|
*/
|
|
import { describe, it, expect } from 'vitest';
|
|
import Parser from 'tree-sitter';
|
|
import TypeScript from 'tree-sitter-typescript';
|
|
import Go from 'tree-sitter-go';
|
|
import Rust from 'tree-sitter-rust';
|
|
import CSharp from 'tree-sitter-c-sharp/bindings/node/index.js';
|
|
import Ruby from 'tree-sitter-ruby';
|
|
import CPP from 'tree-sitter-cpp';
|
|
import PHP from 'tree-sitter-php';
|
|
import { SupportedLanguages } from '../../src/config/supported-languages.js';
|
|
import { getProvider } from '../../src/core/ingestion/languages/index.js';
|
|
import { DOC_BEARING_LABELS } from '../../src/core/ingestion/utils/ast-helpers.js';
|
|
import { EMBEDDABLE_LABELS } from '../../src/core/embeddings/types.js';
|
|
import type { CaptureMap } from '../../src/core/ingestion/language-provider.js';
|
|
import type { NodeLabel } from 'gitnexus-shared';
|
|
|
|
function describeFromProvider(
|
|
language: SupportedLanguages,
|
|
grammar: Parser.Language,
|
|
src: string,
|
|
nodeType: string,
|
|
captureKey: string,
|
|
label: NodeLabel,
|
|
name: string,
|
|
): string | undefined {
|
|
const parser = new Parser();
|
|
parser.setLanguage(grammar);
|
|
const node = parser.parse(src).rootNode.descendantsOfType(nodeType)[0];
|
|
expect(node, `expected a ${nodeType} node for ${language}`).toBeDefined();
|
|
const captureMap: CaptureMap = { [captureKey]: node };
|
|
return getProvider(language).descriptionExtractor?.(label, name, captureMap);
|
|
}
|
|
|
|
// Languages that should surface a description (everything except Vue/Cobol).
|
|
const DOCUMENTABLE_LANGUAGES: readonly SupportedLanguages[] = [
|
|
SupportedLanguages.JavaScript,
|
|
SupportedLanguages.TypeScript,
|
|
SupportedLanguages.Python,
|
|
SupportedLanguages.Java,
|
|
SupportedLanguages.C,
|
|
SupportedLanguages.CPlusPlus,
|
|
SupportedLanguages.CSharp,
|
|
SupportedLanguages.Go,
|
|
SupportedLanguages.Ruby,
|
|
SupportedLanguages.Rust,
|
|
SupportedLanguages.PHP,
|
|
SupportedLanguages.Kotlin,
|
|
SupportedLanguages.Swift,
|
|
SupportedLanguages.Dart,
|
|
];
|
|
|
|
describe('DOC_BEARING_LABELS is bounded to embeddable labels (issue #2270 review fix)', () => {
|
|
it('every doc-bearing label is in EMBEDDABLE_LABELS so its description is searchable', () => {
|
|
const embeddable = new Set<string>(EMBEDDABLE_LABELS);
|
|
const notEmbeddable = [...DOC_BEARING_LABELS].filter((label) => !embeddable.has(label));
|
|
expect(notEmbeddable).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe('leading-doc descriptionExtractor — registration coverage', () => {
|
|
it.each(DOCUMENTABLE_LANGUAGES)('%s provider registers descriptionExtractor', (language) => {
|
|
expect(getProvider(language).descriptionExtractor).toBeDefined();
|
|
});
|
|
});
|
|
|
|
describe('leading-doc descriptionExtractor — behavior per comment family', () => {
|
|
it('TypeScript JSDoc block', () => {
|
|
const d = describeFromProvider(
|
|
SupportedLanguages.TypeScript,
|
|
TypeScript.typescript,
|
|
`/** Adds two numbers, marker TSMARK. */\nfunction add(a: number, b: number) { return a + b; }`,
|
|
'function_declaration',
|
|
'definition.function',
|
|
'Function',
|
|
'add',
|
|
);
|
|
expect(d).toContain('TSMARK');
|
|
});
|
|
|
|
it('Go godoc (// run)', () => {
|
|
const d = describeFromProvider(
|
|
SupportedLanguages.Go,
|
|
Go,
|
|
`package p\n// Add returns the sum, marker GOMARK.\nfunc Add(a int) int { return a }`,
|
|
'function_declaration',
|
|
'definition.function',
|
|
'Function',
|
|
'Add',
|
|
);
|
|
expect(d).toContain('GOMARK');
|
|
});
|
|
|
|
// Go TYPE declarations, not just functions (#2837). Every Go row above uses
|
|
// `function_declaration`/`definition.function`, which anchors on its own
|
|
// declaration node and therefore carries the doc comment as a previous
|
|
// sibling. Types do not: #2837 re-anchored their captures onto the `type_spec`,
|
|
// whose `previousNamedSibling` is null because the comment belongs to the
|
|
// enclosing `type_declaration`. That silently removed the description from
|
|
// every Go struct and interface, and this file — the one place that checks doc
|
|
// extraction across all languages — was structurally incapable of noticing.
|
|
it('Go godoc on a struct declaration', () => {
|
|
const d = describeFromProvider(
|
|
SupportedLanguages.Go,
|
|
Go,
|
|
`package p\n// Wave carries orders, marker GOSTRUCT.\ntype Wave struct { n int }`,
|
|
'type_spec',
|
|
'definition.struct',
|
|
'Struct',
|
|
'Wave',
|
|
);
|
|
expect(d).toContain('GOSTRUCT');
|
|
});
|
|
|
|
it('Go godoc on an interface declaration', () => {
|
|
const d = describeFromProvider(
|
|
SupportedLanguages.Go,
|
|
Go,
|
|
`package p\n// Sink accepts events, marker GOIFACE.\ntype Sink interface { Emit() }`,
|
|
'type_spec',
|
|
'definition.interface',
|
|
'Interface',
|
|
'Sink',
|
|
);
|
|
expect(d).toContain('GOIFACE');
|
|
});
|
|
|
|
// A type declared inside a grouped `type (...)` block with its OWN doc comment
|
|
// must get that comment. Here the doc IS a previous sibling of the `type_spec`
|
|
// (verified against the real grammar), so this exercises the direct path while
|
|
// the two rows above exercise the `wrapperNodeTypes` fallback.
|
|
//
|
|
// The documented type is deliberately the FIRST spec in the block: the helper
|
|
// resolves `descendantsOfType(nodeType)[0]`, so a doc on a later spec would be
|
|
// read off the wrong node and the row would assert nothing.
|
|
it('Go godoc on a type inside a grouped declaration', () => {
|
|
const d = describeFromProvider(
|
|
SupportedLanguages.Go,
|
|
Go,
|
|
`package p\ntype (\n\t// First holds state, marker GOGROUPED.\n\tFirst struct { a int }\n\n\tSecond struct { b int }\n)`,
|
|
'type_spec',
|
|
'definition.struct',
|
|
'Struct',
|
|
'First',
|
|
);
|
|
expect(d).toContain('GOGROUPED');
|
|
});
|
|
|
|
it('Rust /// doc comment', () => {
|
|
const d = describeFromProvider(
|
|
SupportedLanguages.Rust,
|
|
Rust,
|
|
`/// Adds things, marker RSMARK.\nfn add(a: i32) -> i32 { a }`,
|
|
'function_item',
|
|
'definition.function',
|
|
'Function',
|
|
'add',
|
|
);
|
|
expect(d).toContain('RSMARK');
|
|
});
|
|
|
|
it('C# /// XML doc comment', () => {
|
|
const d = describeFromProvider(
|
|
SupportedLanguages.CSharp,
|
|
CSharp,
|
|
`class C {\n/// <summary>Adds, marker CSMARK</summary>\nvoid M() {}\n}`,
|
|
'method_declaration',
|
|
'definition.method',
|
|
'Method',
|
|
'M',
|
|
);
|
|
expect(d).toContain('CSMARK');
|
|
});
|
|
|
|
it('Ruby # comment run', () => {
|
|
const d = describeFromProvider(
|
|
SupportedLanguages.Ruby,
|
|
Ruby,
|
|
`# Adds things, marker RBMARK.\ndef add(a)\n a\nend`,
|
|
'method',
|
|
'definition.method',
|
|
'Method',
|
|
'add',
|
|
);
|
|
expect(d).toContain('RBMARK');
|
|
});
|
|
|
|
it('C++ Doxygen block', () => {
|
|
const d = describeFromProvider(
|
|
SupportedLanguages.CPlusPlus,
|
|
CPP,
|
|
`/** Adds, marker CPPMARK. */\nint add(int a) { return a; }`,
|
|
'function_definition',
|
|
'definition.function',
|
|
'Function',
|
|
'add',
|
|
);
|
|
expect(d).toContain('CPPMARK');
|
|
});
|
|
|
|
it('PHP docblock fallback (non-Eloquent prose)', () => {
|
|
const d = describeFromProvider(
|
|
SupportedLanguages.PHP,
|
|
PHP.php,
|
|
`<?php\nclass C {\n/** Adds, marker PHPMARK. */\npublic function add($a) { return $a; }\n}`,
|
|
'method_declaration',
|
|
'definition.method',
|
|
'Method',
|
|
'add',
|
|
);
|
|
expect(d).toContain('PHPMARK');
|
|
});
|
|
|
|
it('returns undefined for a function with no leading doc comment (Go)', () => {
|
|
const d = describeFromProvider(
|
|
SupportedLanguages.Go,
|
|
Go,
|
|
`package p\nfunc Bare() {}`,
|
|
'function_declaration',
|
|
'definition.function',
|
|
'Function',
|
|
'Bare',
|
|
);
|
|
expect(d).toBeUndefined();
|
|
});
|
|
|
|
it('collects a multi-line /// run (Rust)', () => {
|
|
const d = describeFromProvider(
|
|
SupportedLanguages.Rust,
|
|
Rust,
|
|
`/// First line.\n/// Second line, marker MULTILINE.\nfn add(a: i32) -> i32 { a }`,
|
|
'function_item',
|
|
'definition.function',
|
|
'Function',
|
|
'add',
|
|
);
|
|
expect(d).toContain('First line');
|
|
expect(d).toContain('MULTILINE');
|
|
});
|
|
|
|
it('does NOT attach a Rust //! inner doc to the following item (inner-doc semantics)', () => {
|
|
const d = describeFromProvider(
|
|
SupportedLanguages.Rust,
|
|
Rust,
|
|
`//! Inner doc, marker INNERMARK.\nfn add(a: i32) -> i32 { a }`,
|
|
'function_item',
|
|
'definition.function',
|
|
'Function',
|
|
'add',
|
|
);
|
|
expect(d).toBeUndefined();
|
|
});
|
|
|
|
it('does NOT attach a Rust /*! inner block doc to the following item', () => {
|
|
const d = describeFromProvider(
|
|
SupportedLanguages.Rust,
|
|
Rust,
|
|
`/*! Inner block doc, marker INNERBLOCK. */\nfn add(a: i32) -> i32 { a }`,
|
|
'function_item',
|
|
'definition.function',
|
|
'Function',
|
|
'add',
|
|
);
|
|
expect(d).toBeUndefined();
|
|
});
|
|
|
|
it('collects a /*! Doxygen bang block (C++)', () => {
|
|
const d = describeFromProvider(
|
|
SupportedLanguages.CPlusPlus,
|
|
CPP,
|
|
`/*! Bang block, marker BANGMARK. */\nint add(int a) { return a; }`,
|
|
'function_definition',
|
|
'definition.function',
|
|
'Function',
|
|
'add',
|
|
);
|
|
expect(d).toContain('BANGMARK');
|
|
});
|
|
});
|
|
|
|
describe('leading-doc descriptionExtractor — exported TS/JS decls (issue #2270 review fix)', () => {
|
|
it('attaches JSDoc to an exported function (JSDoc precedes export_statement)', () => {
|
|
const d = describeFromProvider(
|
|
SupportedLanguages.TypeScript,
|
|
TypeScript.typescript,
|
|
`/** Exported adder, marker EXPFN. */\nexport function add(a: number) { return a; }`,
|
|
'function_declaration',
|
|
'definition.function',
|
|
'Function',
|
|
'add',
|
|
);
|
|
expect(d).toContain('EXPFN');
|
|
});
|
|
|
|
it('attaches JSDoc to an exported class', () => {
|
|
const d = describeFromProvider(
|
|
SupportedLanguages.TypeScript,
|
|
TypeScript.typescript,
|
|
`/** Exported widget, marker EXPCLASS. */\nexport class Widget { run() {} }`,
|
|
'class_declaration',
|
|
'definition.class',
|
|
'Class',
|
|
'Widget',
|
|
);
|
|
expect(d).toContain('EXPCLASS');
|
|
});
|
|
|
|
it('attaches JSDoc to an export default function', () => {
|
|
const d = describeFromProvider(
|
|
SupportedLanguages.TypeScript,
|
|
TypeScript.typescript,
|
|
`/** Default export, marker EXPDEFAULT. */\nexport default function add(a: number) { return a; }`,
|
|
'function_declaration',
|
|
'definition.function',
|
|
'Function',
|
|
'add',
|
|
);
|
|
expect(d).toContain('EXPDEFAULT');
|
|
});
|
|
|
|
it('still attaches JSDoc to a bare (non-exported) function', () => {
|
|
const d = describeFromProvider(
|
|
SupportedLanguages.TypeScript,
|
|
TypeScript.typescript,
|
|
`/** Bare adder, marker BAREFN. */\nfunction add(a: number) { return a; }`,
|
|
'function_declaration',
|
|
'definition.function',
|
|
'Function',
|
|
'add',
|
|
);
|
|
expect(d).toContain('BAREFN');
|
|
});
|
|
});
|
|
|
|
describe('leading-doc descriptionExtractor — line-comment adjacency (issue #2270 review fix)', () => {
|
|
it('does NOT attach a // comment separated from the function by a blank line (Go)', () => {
|
|
const d = describeFromProvider(
|
|
SupportedLanguages.Go,
|
|
Go,
|
|
`package p\n// Detached note, marker DETACHED.\n\nfunc Add(a int) int { return a }`,
|
|
'function_declaration',
|
|
'definition.function',
|
|
'Function',
|
|
'Add',
|
|
);
|
|
expect(d).toBeUndefined();
|
|
});
|
|
|
|
it('collects only the adjacent // block when an earlier block is blank-separated (Go)', () => {
|
|
const d = describeFromProvider(
|
|
SupportedLanguages.Go,
|
|
Go,
|
|
`package p\n// Unrelated earlier block, marker EARLIER.\n\n// Adjacent doc, marker ADJACENT.\nfunc Add(a int) int { return a }`,
|
|
'function_declaration',
|
|
'definition.function',
|
|
'Function',
|
|
'Add',
|
|
);
|
|
expect(d).toContain('ADJACENT');
|
|
expect(d).not.toContain('EARLIER');
|
|
});
|
|
|
|
it('does NOT absorb a Ruby magic comment separated from the first method by a blank line', () => {
|
|
const d = describeFromProvider(
|
|
SupportedLanguages.Ruby,
|
|
Ruby,
|
|
`# frozen_string_literal: true\n\ndef add(a)\n a\nend`,
|
|
'method',
|
|
'definition.method',
|
|
'Method',
|
|
'add',
|
|
);
|
|
expect(d).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe('leading-doc descriptionExtractor — directive & magic comments (issue #2270 review fix)', () => {
|
|
it('does NOT absorb a Go //go: build directive directly above a function', () => {
|
|
const d = describeFromProvider(
|
|
SupportedLanguages.Go,
|
|
Go,
|
|
`package p\n//go:build linux\nfunc Add(a int) int { return a }`,
|
|
'function_declaration',
|
|
'definition.function',
|
|
'Function',
|
|
'Add',
|
|
);
|
|
expect(d).toBeUndefined();
|
|
});
|
|
|
|
it('keeps a real godoc line and skips an interleaved //go:generate directive', () => {
|
|
const d = describeFromProvider(
|
|
SupportedLanguages.Go,
|
|
Go,
|
|
`package p\n// Add returns the sum, marker GODOC.\n//go:generate stringer -type=T\nfunc Add(a int) int { return a }`,
|
|
'function_declaration',
|
|
'definition.function',
|
|
'Function',
|
|
'Add',
|
|
);
|
|
expect(d).toContain('GODOC');
|
|
expect(d).not.toContain('go:generate');
|
|
});
|
|
|
|
it('does NOT absorb a Ruby magic comment directly above the first method (no blank line)', () => {
|
|
const d = describeFromProvider(
|
|
SupportedLanguages.Ruby,
|
|
Ruby,
|
|
`# frozen_string_literal: true\ndef add(a)\n a\nend`,
|
|
'method',
|
|
'definition.method',
|
|
'Method',
|
|
'add',
|
|
);
|
|
expect(d).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe('phpDescriptionExtractor — Eloquent metadata wins over PHPDoc fallback', () => {
|
|
it('returns the Eloquent relation, not the docblock prose, when both are present', () => {
|
|
const d = describeFromProvider(
|
|
SupportedLanguages.PHP,
|
|
PHP.php,
|
|
`<?php\nclass User {\n/** Prose docblock, marker DOCPROSE. */\npublic function orders() { return $this->hasMany(Order::class); }\n}`,
|
|
'method_declaration',
|
|
'definition.method',
|
|
'Method',
|
|
'orders',
|
|
);
|
|
expect(d).toBe('hasMany(Order)');
|
|
expect(d).not.toContain('DOCPROSE');
|
|
});
|
|
});
|