ucn 5.3.7 → 5.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/ucn/SKILL.md +7 -1
- package/.claude/skills/ucn/references/commands.md +9 -1
- package/cli/index.js +4 -2
- package/core/cache.js +2 -1
- package/core/execute.js +6 -2
- package/core/graph.js +6 -2
- package/core/output/doctor.js +1 -1
- package/core/output/lines.js +8 -2
- package/core/output/public.js +3 -0
- package/core/output/reporting.js +1 -1
- package/core/output/search.js +6 -1
- package/core/registry.js +1 -1
- package/core/reporting.js +2 -0
- package/core/search.js +4 -1
- package/languages/c-family.js +9 -8
- package/languages/csharp.js +11 -10
- package/languages/go.js +8 -7
- package/languages/java.js +3 -2
- package/languages/javascript.js +2 -1
- package/languages/python.js +3 -2
- package/languages/rust.js +3 -2
- package/languages/utils.js +51 -29
- package/package.json +2 -2
|
@@ -126,7 +126,7 @@ answers:
|
|
|
126
126
|
ucn find handleRequest --lines # path:line:signature # kind
|
|
127
127
|
ucn show handleRequest --lines # callers as path:line:text; unverified ones end in "\t# unverified: <reason>"
|
|
128
128
|
ucn show handleRequest --lines --sections=callees
|
|
129
|
-
ucn usages handleRequest --lines # every literal-name
|
|
129
|
+
ucn usages handleRequest --lines # every literal-name occurrence; non-call kinds tagged "# import" / "# definition"
|
|
130
130
|
ucn search 'retry(' --lines # grep -n output, code-aware scope
|
|
131
131
|
ucn impact handleRequest --lines
|
|
132
132
|
ucn source handleRequest --raw # the code and nothing else, ready for an exact-string edit
|
|
@@ -136,6 +136,12 @@ ucn source src/server.js:40-80 --raw
|
|
|
136
136
|
Records go to stdout; the `ACCOUNT` / `CONTRACT` lines, notes, and the
|
|
137
137
|
same-name disambiguation go to stderr prefixed `# ` (MCP keeps them in the one
|
|
138
138
|
text block, and `--raw` appends its note as one trailing `# ` line there).
|
|
139
|
+
`usages` emits one record per occurrence, so a source line may repeat; deduplicate
|
|
140
|
+
`path:line` values for line counts. Definition handles start at decorators when
|
|
141
|
+
present, while usages point at token lines (`nameLine` identifies the declaration
|
|
142
|
+
token when it differs from `startLine`). Structural `search --unused` keeps its
|
|
143
|
+
safety note and decorator tags in shell output; runtime registrations can appear
|
|
144
|
+
and zero call edges do not prove a symbol is safe to delete.
|
|
139
145
|
`--lines` lists the whole band without default row/character caps, so pipe through
|
|
140
146
|
`grep -v '# unverified'` for the confirmed tier or `cut -d: -f1 | sort | uniq -c`
|
|
141
147
|
for callers per file. Nothing to list prints nothing and exits 1, grep's
|
|
@@ -33,7 +33,7 @@ structural or code-only search.
|
|
|
33
33
|
|---|---|
|
|
34
34
|
| `repo` | Repository orientation. Select `summary,files,stats,health` with `--sections`; `--deep` includes readiness evidence. Skipped unsupported source is listed with a grep/language-tool handoff. |
|
|
35
35
|
| `deps <file>` | File dependency graph. Use `--direction=imports\|importers\|both`, `--detailed`, or `--cycles`. Cycles distinguish eager edges from function-local, Python typing-guarded, and TypeScript type-only edges. Complete cycle groups remain visible when enumeration is capped. |
|
|
36
|
-
| `api [file]` | Static exported/public surface for a project or file. |
|
|
36
|
+
| `api [file]` | Static exported/public surface for a project or file. An exact file includes tests; broader scans exclude tests with a count. Use `--include-tests` to include them. |
|
|
37
37
|
| `entrypoints` | Framework, route, task, test, and runtime entry points. |
|
|
38
38
|
| `endpoints` | Server/client HTTP surface; `--bridge` adds advisory matching. |
|
|
39
39
|
|
|
@@ -49,6 +49,14 @@ structural or code-only search.
|
|
|
49
49
|
|
|
50
50
|
Symbol-listing commands emit handles such as `src/api.ts:42:handler`. Pass the full handle to symbol commands. `path:line` also works. Handles prevent same-named definitions from being silently combined.
|
|
51
51
|
|
|
52
|
+
Definition handles and source spans start at the first decorator or annotation when present; literal usages point at the actual token line. Use a symbol's `nameLine` (when present, otherwise `startLine`) to compare declaration tokens with usages.
|
|
53
|
+
|
|
54
|
+
Structural `search --param` matches parameter names, types, and defaults; `--returns` matches return annotations. Both exclude AST comments and preserve string contents. `--unused` lists callable symbols without call edges, not safe-delete candidates; decorated runtime registrations may still appear. Its safety note and decorator tags are retained in `--lines` output. Use `deadcode` and `usages` before deletion.
|
|
55
|
+
|
|
56
|
+
`repo` summary/stats `buildTime` is the duration of the last index build (discovery, parsing, and graphs), retained in the cache. It excludes cache loading/saving and query execution, so it is not command wall time; `buildTimeNote` states this boundary.
|
|
57
|
+
|
|
58
|
+
`--lines` writes one record per output line. `usages` records occurrences, so multiple tokens on the same source line can produce repeated `path:line` values. Deduplicate those values when counting source lines.
|
|
59
|
+
|
|
52
60
|
## Common flags
|
|
53
61
|
|
|
54
62
|
| Flag | Meaning |
|
package/cli/index.js
CHANGED
|
@@ -917,7 +917,8 @@ Commands:
|
|
|
917
917
|
deps <file> File graph; --direction=imports|importers|both
|
|
918
918
|
--detailed Include import declarations
|
|
919
919
|
deps --cycles Report circular dependencies (no file target)
|
|
920
|
-
api [file] Project or file public API
|
|
920
|
+
api [file] Project or file public API (exact files include tests;
|
|
921
|
+
broader scans exclude tests unless --include-tests)
|
|
921
922
|
check [symbol] Signature check; without symbol, precommit check
|
|
922
923
|
plan <symbol> Preview rename or parameter edits
|
|
923
924
|
entrypoints Runtime and framework entry points
|
|
@@ -933,7 +934,8 @@ Common flags:
|
|
|
933
934
|
--base=REF --staged --no-cache --clear-cache [--all] --max-files=N --workers=N
|
|
934
935
|
--max-chars=N (text output; default 10K targeted / 3K broad, ceiling 100K)
|
|
935
936
|
--lines find/usages/search/show/impact: grep -n shape, one path:line:text
|
|
936
|
-
record per line
|
|
937
|
+
record per output line; usages may repeat a source line per occurrence
|
|
938
|
+
(tags after a tab: # unverified: <reason>, # import,
|
|
937
939
|
# callee); accounting and notes go to stderr as "# " lines; exit 1
|
|
938
940
|
when nothing matched; exit 2 on errors. No default result cap.
|
|
939
941
|
show defaults to callers; --sections=callers,callees selects bands.
|
package/core/cache.js
CHANGED
|
@@ -712,7 +712,8 @@ function clearAllCaches() {
|
|
|
712
712
|
// copied bindings and qualified macro receivers; TS indexed array evidence.
|
|
713
713
|
// v225 (fix #357): Rust `use path::name as local` bindings record the original name with a paired `renames` alias.
|
|
714
714
|
// v226: bundled/minified filename exclusions are disclosed in discoveryIssues.
|
|
715
|
-
|
|
715
|
+
// v227: signature parameter/return text excludes AST comments in every language.
|
|
716
|
+
const CACHE_FORMAT_VERSION = 227;
|
|
716
717
|
const USAGE_CACHE_FILE = 'usage-results.json';
|
|
717
718
|
|
|
718
719
|
/**
|
package/core/execute.js
CHANGED
|
@@ -2330,7 +2330,7 @@ const HANDLERS = {
|
|
|
2330
2330
|
},
|
|
2331
2331
|
|
|
2332
2332
|
api: (index, p) => {
|
|
2333
|
-
if (p.file) {
|
|
2333
|
+
if (p.file && typeof index.resolveFilePathForQuery(p.file) !== 'string') {
|
|
2334
2334
|
const fileErr = checkFilePatternMatch(index, p.file);
|
|
2335
2335
|
if (fileErr) return { ok: false, error: fileErr };
|
|
2336
2336
|
}
|
|
@@ -2346,7 +2346,7 @@ const HANDLERS = {
|
|
|
2346
2346
|
return { ok: false, error: `No files matched the 'in' directory filter '${p.in}'.` };
|
|
2347
2347
|
}
|
|
2348
2348
|
}
|
|
2349
|
-
let result = index.api(p.file, { in: p.in });
|
|
2349
|
+
let result = index.api(p.file, { in: p.in, includeTests: p.includeTests });
|
|
2350
2350
|
if (p.file) {
|
|
2351
2351
|
const fileErr = checkFileError(result, p.file, index);
|
|
2352
2352
|
if (fileErr) return { ok: false, error: fileErr };
|
|
@@ -2374,6 +2374,10 @@ const HANDLERS = {
|
|
|
2374
2374
|
}
|
|
2375
2375
|
result = items;
|
|
2376
2376
|
}
|
|
2377
|
+
if (result.apiInfo?.excludedTestFiles > 0) {
|
|
2378
|
+
const excluded = `${result.apiInfo.excludedTestFiles} test file(s) excluded from API; use --include-tests to include them, or name an exact file.`;
|
|
2379
|
+
note = note ? `${note}\n${excluded}` : excluded;
|
|
2380
|
+
}
|
|
2377
2381
|
return { ok: true, result, note };
|
|
2378
2382
|
},
|
|
2379
2383
|
|
package/core/graph.js
CHANGED
|
@@ -564,6 +564,8 @@ function api(index, filePath, options = {}) {
|
|
|
564
564
|
const results = [];
|
|
565
565
|
let scopedFiles = 0;
|
|
566
566
|
let pythonImplicitFiles = 0;
|
|
567
|
+
let excludedTestFiles = 0;
|
|
568
|
+
let explicitFile = false;
|
|
567
569
|
|
|
568
570
|
let fileIterator;
|
|
569
571
|
if (filePath) {
|
|
@@ -573,6 +575,7 @@ function api(index, filePath, options = {}) {
|
|
|
573
575
|
const fileEntry = index.files.get(resolved);
|
|
574
576
|
if (!fileEntry) return { error: 'file-not-found', filePath };
|
|
575
577
|
fileIterator = [[resolved, fileEntry]];
|
|
578
|
+
explicitFile = true;
|
|
576
579
|
} else {
|
|
577
580
|
// Fall back to pattern filter (substring match on relative path)
|
|
578
581
|
const matches = [];
|
|
@@ -596,7 +599,8 @@ function api(index, filePath, options = {}) {
|
|
|
596
599
|
}
|
|
597
600
|
|
|
598
601
|
// Skip test files by default (test classes aren't part of public API)
|
|
599
|
-
if (!options.includeTests && isTestFile(fileEntry.relativePath, fileEntry.language)) {
|
|
602
|
+
if (!explicitFile && !options.includeTests && isTestFile(fileEntry.relativePath, fileEntry.language)) {
|
|
603
|
+
excludedTestFiles++;
|
|
600
604
|
continue;
|
|
601
605
|
}
|
|
602
606
|
scopedFiles++;
|
|
@@ -612,7 +616,7 @@ function api(index, filePath, options = {}) {
|
|
|
612
616
|
results.sort((a, b) => codeUnitCompare(a.file, b.file) ||
|
|
613
617
|
(a.startLine - b.startLine) || codeUnitCompare(a.name, b.name));
|
|
614
618
|
Object.defineProperty(results, 'apiInfo', {
|
|
615
|
-
value: { scopedFiles, pythonImplicitFiles },
|
|
619
|
+
value: { scopedFiles, pythonImplicitFiles, excludedTestFiles },
|
|
616
620
|
enumerable: false, writable: true, configurable: true,
|
|
617
621
|
});
|
|
618
622
|
return results;
|
package/core/output/doctor.js
CHANGED
|
@@ -33,7 +33,7 @@ function formatDoctor(result, options = {}) {
|
|
|
33
33
|
// Cache state
|
|
34
34
|
if (result.cache) {
|
|
35
35
|
const state = result.cache.fresh === true ? 'fresh' : result.cache.fresh === false ? 'stale' : 'unknown';
|
|
36
|
-
const buildHint = result.cache.buildMs ? `, ${result.cache.buildMs}ms build` : '';
|
|
36
|
+
const buildHint = result.cache.buildMs ? `, ${result.cache.buildMs}ms last index build (excludes cache I/O and query execution)` : '';
|
|
37
37
|
lines.push(`Cache: ${state}${buildHint}`);
|
|
38
38
|
}
|
|
39
39
|
if (result.commandTrust) {
|
package/core/output/lines.js
CHANGED
|
@@ -29,7 +29,8 @@ function record(pathLike, line, text, tag = '') {
|
|
|
29
29
|
// Keep unusual filenames from becoming notes or extra physical records.
|
|
30
30
|
let file = String(pathLike).replace(/\\/g, '\\\\').replace(/\n/g, '\\n').replace(/\r/g, '\\r').replace(/\t/g, '\\t');
|
|
31
31
|
if (file.startsWith('# ')) file = './' + file;
|
|
32
|
-
|
|
32
|
+
const tagText = String(tag || '').replace(/\s+/g, ' ').trim();
|
|
33
|
+
return `${file}:${line == null ? 0 : line}:${body}${tagText ? `\t# ${tagText}` : ''}`;
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
function commentLines(text) {
|
|
@@ -95,9 +96,14 @@ function searchRecords(result) {
|
|
|
95
96
|
if (result && !Array.isArray(result) && Array.isArray(result.results)) {
|
|
96
97
|
for (const item of result.results) {
|
|
97
98
|
const text = item.params != null ? `${item.name}(${item.params})` : item.name;
|
|
98
|
-
|
|
99
|
+
const decorators = (item.decorators || []).map(d => `@${String(d).replace(/^@/, '')}`).join(', ');
|
|
100
|
+
const tag = [item.kind || item.type, decorators].filter(Boolean).join('; ');
|
|
101
|
+
out.push(record(item.file, item.line, text, tag));
|
|
99
102
|
}
|
|
100
103
|
const meta = result.meta;
|
|
104
|
+
if (meta?.query?.unused) {
|
|
105
|
+
notes.push(...commentLines(require('./search').unusedSearchNote()));
|
|
106
|
+
}
|
|
101
107
|
if (meta && meta.totalMatched > meta.shown) {
|
|
102
108
|
notes.push(`# ${meta.totalMatched - meta.shown} more match(es) (--limit=N / --all)`);
|
|
103
109
|
}
|
package/core/output/public.js
CHANGED
|
@@ -424,6 +424,9 @@ function formatPublicJson(command, result, params = {}, execution = {}) {
|
|
|
424
424
|
if (result.meta.truncatedMatches > 0) commandMeta.truncated = true;
|
|
425
425
|
if (result.unsupportedMatches) commandMeta.unsupportedMatches = result.unsupportedMatches;
|
|
426
426
|
}
|
|
427
|
+
if (command === 'api' && result?.apiInfo) {
|
|
428
|
+
commandMeta.apiInfo = result.apiInfo;
|
|
429
|
+
}
|
|
427
430
|
if (command === 'entrypoints' && result?.filterInfo) {
|
|
428
431
|
commandMeta.hiddenTestEntrypoints = result.filterInfo.hiddenTests;
|
|
429
432
|
commandMeta.testsIncluded = result.filterInfo.testsIncluded;
|
package/core/output/reporting.js
CHANGED
|
@@ -125,7 +125,7 @@ function formatStats(stats, options = {}) {
|
|
|
125
125
|
lines.push(`Files: ${stats.files}`);
|
|
126
126
|
}
|
|
127
127
|
lines.push(`Symbols: ${stats.symbols}`);
|
|
128
|
-
lines.push(`
|
|
128
|
+
lines.push(`Last index build: ${stats.buildTime}ms (excludes cache I/O and query execution; reused from cache)`);
|
|
129
129
|
|
|
130
130
|
lines.push('\nBy Language:');
|
|
131
131
|
for (const [lang, info] of Object.entries(stats.byLanguage)) {
|
package/core/output/search.js
CHANGED
|
@@ -87,6 +87,10 @@ function formatSearchJson(results, term) {
|
|
|
87
87
|
/**
|
|
88
88
|
* Format structural search results (index-based queries)
|
|
89
89
|
*/
|
|
90
|
+
function unusedSearchNote(flag = '--unused') {
|
|
91
|
+
return `${flag} lists callable symbols with no resolved call edge; it does not assess type/field/reference liveness and is not safe-delete proof. Confirm with deadcode and usages.`;
|
|
92
|
+
}
|
|
93
|
+
|
|
90
94
|
function formatStructuralSearch(result, options = {}) {
|
|
91
95
|
const { results, meta } = result;
|
|
92
96
|
const lines = [];
|
|
@@ -106,7 +110,7 @@ function formatStructuralSearch(result, options = {}) {
|
|
|
106
110
|
lines.push(`Structural search: ${queryStr}`);
|
|
107
111
|
lines.push('═'.repeat(60));
|
|
108
112
|
if (meta.query.unused) {
|
|
109
|
-
lines.push(`NOTE: ${options.unusedFlag
|
|
113
|
+
lines.push(`NOTE: ${unusedSearchNote(options.unusedFlag)}`);
|
|
110
114
|
lines.push('');
|
|
111
115
|
}
|
|
112
116
|
|
|
@@ -447,6 +451,7 @@ function formatTestsJson(tests, name) {
|
|
|
447
451
|
}
|
|
448
452
|
|
|
449
453
|
module.exports = {
|
|
454
|
+
unusedSearchNote,
|
|
450
455
|
formatSearch,
|
|
451
456
|
formatSearchJson,
|
|
452
457
|
formatStructuralSearch,
|
package/core/registry.js
CHANGED
|
@@ -143,7 +143,7 @@ const FLAG_APPLICABILITY = {
|
|
|
143
143
|
impact: ['name', 'file', 'exclude', 'className', 'line', 'includeMethods', 'top', 'unreachableOnly', 'compact', 'base', 'staged', 'limit', 'all', 'lines'],
|
|
144
144
|
tests: ['name', 'file', 'exclude', 'className', 'line', 'callsOnly', 'depth', 'includeMethods', 'all'],
|
|
145
145
|
deps: ['file', 'exclude', 'depth', 'direction', 'all', 'detailed', 'cycles'],
|
|
146
|
-
api: ['file', 'in', 'limit'],
|
|
146
|
+
api: ['file', 'in', 'limit', 'includeTests'],
|
|
147
147
|
check: ['name', 'file', 'className', 'line', 'includeMethods', 'base', 'staged', 'limit'],
|
|
148
148
|
plan: ['name', 'file', 'className', 'line', 'addParam', 'removeParam', 'renameTo', 'defaultValue'],
|
|
149
149
|
repo: ['file', 'exclude', 'top', 'limit', 'all', 'detailed', 'topLevel', 'in', 'functions', 'hot', 'deep', 'sections'],
|
package/core/reporting.js
CHANGED
|
@@ -48,6 +48,7 @@ function getStats(index, options = {}) {
|
|
|
48
48
|
files: scopedFiles.length,
|
|
49
49
|
symbols: totalSymbols, // Total symbol count, not unique names
|
|
50
50
|
buildTime: index.buildTime,
|
|
51
|
+
buildTimeNote: 'Last index build, including discovery and graphs; excludes cache I/O and query execution. Reused when loading a cached index.',
|
|
51
52
|
byLanguage: {},
|
|
52
53
|
byType: {},
|
|
53
54
|
...(index.truncated && { truncated: index.truncated })
|
|
@@ -1044,6 +1045,7 @@ function orient(index, options = {}) {
|
|
|
1044
1045
|
files: stats.files,
|
|
1045
1046
|
symbols: stats.symbols,
|
|
1046
1047
|
buildTime: stats.buildTime,
|
|
1048
|
+
buildTimeNote: stats.buildTimeNote,
|
|
1047
1049
|
byLanguage: stats.byLanguage,
|
|
1048
1050
|
dirs,
|
|
1049
1051
|
hot: {
|
package/core/search.js
CHANGED
|
@@ -883,7 +883,10 @@ function structuralSearch(index, options = {}) {
|
|
|
883
883
|
// expression position — never "unused" (the deadcode
|
|
884
884
|
// twin of the bodyScopedName audit skip).
|
|
885
885
|
if (def.bodyScopedName) continue;
|
|
886
|
-
|
|
886
|
+
// buildCalleeIndex rebuilds the whole project. Reuse
|
|
887
|
+
// the eagerly built (or cache-loaded) index, including
|
|
888
|
+
// across every candidate in this operation.
|
|
889
|
+
if (!index.calleeIndex) index.buildCalleeIndex();
|
|
887
890
|
// A name whose every call site is its own recursion
|
|
888
891
|
// has zero callers (fix #253c — the deadcode
|
|
889
892
|
// carve-out, applied here). Class-kind names are
|
package/languages/c-family.js
CHANGED
|
@@ -13,6 +13,7 @@ const { typeOrigin } = require('./type-evidence');
|
|
|
13
13
|
|
|
14
14
|
const {
|
|
15
15
|
traverseTree,
|
|
16
|
+
nodeTextWithoutComments,
|
|
16
17
|
traverseTreeCached,
|
|
17
18
|
nodeToLocation,
|
|
18
19
|
extractJSDocstring,
|
|
@@ -989,7 +990,7 @@ function paramTypeText(param, identity) {
|
|
|
989
990
|
const defaultValue = param.childForFieldName('default_value');
|
|
990
991
|
const end = defaultValue ? defaultValue.startIndex : param.endIndex;
|
|
991
992
|
if (identity.nameNode.startIndex < base || identity.nameNode.endIndex > end) return null;
|
|
992
|
-
const text = param.
|
|
993
|
+
const text = nodeTextWithoutComments(param).slice(0, end - base);
|
|
993
994
|
const typeText = (text.slice(0, identity.nameNode.startIndex - base) +
|
|
994
995
|
text.slice(identity.nameNode.endIndex - base))
|
|
995
996
|
.replace(/\s+/g, ' ')
|
|
@@ -1014,10 +1015,10 @@ function structuredParams(paramsNode) {
|
|
|
1014
1015
|
// their type text alone — the type must not double as both name and
|
|
1015
1016
|
// annotation, and `void *` must not collapse into the `(void)` form.
|
|
1016
1017
|
const info = {
|
|
1017
|
-
name: identity.name || param.
|
|
1018
|
+
name: identity.name || nodeTextWithoutComments(param).replace(/\s+/g, ' ').trim(),
|
|
1018
1019
|
};
|
|
1019
1020
|
if (typeNode && identity.name) {
|
|
1020
|
-
info.type = paramTypeText(param, identity) || typeNode
|
|
1021
|
+
info.type = paramTypeText(param, identity) || nodeTextWithoutComments(typeNode);
|
|
1021
1022
|
}
|
|
1022
1023
|
if (param.type === 'optional_parameter_declaration') info.optional = true;
|
|
1023
1024
|
let declaratorCursor = declarator;
|
|
@@ -1164,7 +1165,7 @@ function returnTypeOf(node) {
|
|
|
1164
1165
|
const descriptor = (current.namedChildren || []).find(child =>
|
|
1165
1166
|
child.type === 'type_descriptor') || current.namedChild(0);
|
|
1166
1167
|
const type = descriptor?.childForFieldName('type') || descriptor;
|
|
1167
|
-
return type
|
|
1168
|
+
return nodeTextWithoutComments(type) || null;
|
|
1168
1169
|
}
|
|
1169
1170
|
for (const child of current.namedChildren || []) {
|
|
1170
1171
|
const found = findTrailing(child);
|
|
@@ -1193,7 +1194,7 @@ function returnTypeOf(node) {
|
|
|
1193
1194
|
current = current.childForFieldName('declarator') ||
|
|
1194
1195
|
(current.namedChildren || []).find(child => child.type.endsWith('_declarator'));
|
|
1195
1196
|
}
|
|
1196
|
-
return stars > 0 ? `${typeNode
|
|
1197
|
+
return stars > 0 ? `${nodeTextWithoutComments(typeNode)} ${'*'.repeat(stars)}` : nodeTextWithoutComments(typeNode);
|
|
1197
1198
|
}
|
|
1198
1199
|
|
|
1199
1200
|
function memberFromNode(node, className, access, lines, mode) {
|
|
@@ -1209,7 +1210,7 @@ function memberFromNode(node, className, access, lines, mode) {
|
|
|
1209
1210
|
if (isConstructor && identity.name.startsWith('~')) modifiers.push('destructor');
|
|
1210
1211
|
return {
|
|
1211
1212
|
name: identity.name,
|
|
1212
|
-
params: paramsNode ? paramsNode.
|
|
1213
|
+
params: paramsNode ? nodeTextWithoutComments(paramsNode).replace(/^\(|\)$/g, '').trim() : '...',
|
|
1213
1214
|
paramsStructured: structuredParams(paramsNode),
|
|
1214
1215
|
returnType: isConstructor ? null :
|
|
1215
1216
|
(identity.conversionType || returnTypeOf(node)),
|
|
@@ -1510,7 +1511,7 @@ function findFunctionsInTree(code, tree, mode, sourceLines = null) {
|
|
|
1510
1511
|
: null;
|
|
1511
1512
|
functions.push({
|
|
1512
1513
|
name: identity.name,
|
|
1513
|
-
params: paramsNode ? paramsNode.
|
|
1514
|
+
params: paramsNode ? nodeTextWithoutComments(paramsNode).replace(/^\(|\)$/g, '').trim() : '...',
|
|
1514
1515
|
paramsStructured: structuredParams(paramsNode),
|
|
1515
1516
|
returnType: isConstructor ? null :
|
|
1516
1517
|
(identity.conversionType || returnTypeOf(node)),
|
|
@@ -1800,7 +1801,7 @@ function findMacrosInTree(tree, lines, parser) {
|
|
|
1800
1801
|
startLine,
|
|
1801
1802
|
endLine,
|
|
1802
1803
|
indent,
|
|
1803
|
-
params: paramsNode ? paramsNode.
|
|
1804
|
+
params: paramsNode ? nodeTextWithoutComments(paramsNode).replace(/^\(|\)$/g, '').trim() : undefined,
|
|
1804
1805
|
paramsStructured: paramsNode
|
|
1805
1806
|
? (paramsNode.namedChildren || [])
|
|
1806
1807
|
.filter(child => child.type === 'identifier')
|
package/languages/csharp.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
4
|
traverseTree,
|
|
5
|
+
nodeTextWithoutComments,
|
|
5
6
|
traverseTreeCached,
|
|
6
7
|
nodeToLocation,
|
|
7
8
|
extractJSDocstring,
|
|
@@ -127,7 +128,7 @@ function structuredParams(paramsNode) {
|
|
|
127
128
|
if (nameNode) {
|
|
128
129
|
recoveredParams.push({
|
|
129
130
|
name: nameNode.text,
|
|
130
|
-
...(typeNode && { type: typeNode
|
|
131
|
+
...(typeNode && { type: nodeTextWithoutComments(typeNode) }),
|
|
131
132
|
rest: true,
|
|
132
133
|
});
|
|
133
134
|
}
|
|
@@ -138,14 +139,14 @@ function structuredParams(paramsNode) {
|
|
|
138
139
|
const typeNode = param.childForFieldName('type');
|
|
139
140
|
if (!nameNode) continue;
|
|
140
141
|
const info = { name: nameNode.text };
|
|
141
|
-
if (typeNode) info.type = typeNode
|
|
142
|
+
if (typeNode) info.type = nodeTextWithoutComments(typeNode);
|
|
142
143
|
if (modifiersOf(param).includes('this')) info.extensionReceiver = true;
|
|
143
144
|
if (param.type === 'parameter_array') info.rest = true;
|
|
144
145
|
const value = param.childForFieldName('value') ||
|
|
145
146
|
param.namedChildren.find(child => child !== nameNode && child !== typeNode &&
|
|
146
|
-
!['attribute_list', 'modifier'].includes(child.type));
|
|
147
|
+
!['attribute_list', 'modifier', 'comment'].includes(child.type));
|
|
147
148
|
if (value) {
|
|
148
|
-
info.default = value
|
|
149
|
+
info.default = nodeTextWithoutComments(value);
|
|
149
150
|
info.optional = true;
|
|
150
151
|
}
|
|
151
152
|
params.push(info);
|
|
@@ -208,9 +209,9 @@ function memberFromNode(node, className, lines) {
|
|
|
208
209
|
const paramsStructured = structuredParams(paramsNode);
|
|
209
210
|
return {
|
|
210
211
|
name,
|
|
211
|
-
params: paramsNode ? paramsNode.
|
|
212
|
+
params: paramsNode ? nodeTextWithoutComments(paramsNode).replace(/^\(|\)$/g, '').trim() : '...',
|
|
212
213
|
paramsStructured,
|
|
213
|
-
returnType: isConstructor ? null : returnNode
|
|
214
|
+
returnType: isConstructor ? null : nodeTextWithoutComments(returnNode).trim() || null,
|
|
214
215
|
startLine,
|
|
215
216
|
endLine,
|
|
216
217
|
indent,
|
|
@@ -263,9 +264,9 @@ function indexerMember(node, className, lines) {
|
|
|
263
264
|
const { startLine, endLine, indent } = nodeToLocation(node, lines);
|
|
264
265
|
return {
|
|
265
266
|
name: 'this[]',
|
|
266
|
-
params: paramsNode ? paramsNode.
|
|
267
|
+
params: paramsNode ? nodeTextWithoutComments(paramsNode).replace(/^\[|\]$/g, '').trim() : '...',
|
|
267
268
|
paramsStructured: structuredParams(paramsNode),
|
|
268
|
-
returnType: typeNode
|
|
269
|
+
returnType: nodeTextWithoutComments(typeNode).trim() || null,
|
|
269
270
|
startLine,
|
|
270
271
|
endLine,
|
|
271
272
|
indent,
|
|
@@ -495,9 +496,9 @@ function findFunctions(code, parser) {
|
|
|
495
496
|
const modifiers = modifiersOf(node);
|
|
496
497
|
functions.push({
|
|
497
498
|
name: nameNode.text,
|
|
498
|
-
params: paramsNode ? paramsNode.
|
|
499
|
+
params: paramsNode ? nodeTextWithoutComments(paramsNode).replace(/^\(|\)$/g, '').trim() : '...',
|
|
499
500
|
paramsStructured: structuredParams(paramsNode),
|
|
500
|
-
returnType: returnNode
|
|
501
|
+
returnType: nodeTextWithoutComments(returnNode).trim() || null,
|
|
501
502
|
startLine,
|
|
502
503
|
endLine,
|
|
503
504
|
indent,
|
package/languages/go.js
CHANGED
|
@@ -10,6 +10,7 @@ const { ReceiverTypeMap, typeOrigin } = require('./type-evidence');
|
|
|
10
10
|
|
|
11
11
|
const {
|
|
12
12
|
traverseTree,
|
|
13
|
+
nodeTextWithoutComments,
|
|
13
14
|
traverseTreeCached,
|
|
14
15
|
nodeToLocation,
|
|
15
16
|
parseStructuredParams,
|
|
@@ -29,7 +30,7 @@ function parseTree(parser, code) {
|
|
|
29
30
|
function extractReturnType(node) {
|
|
30
31
|
const resultNode = node.childForFieldName('result');
|
|
31
32
|
if (resultNode) {
|
|
32
|
-
return resultNode.
|
|
33
|
+
return nodeTextWithoutComments(resultNode).trim() || null;
|
|
33
34
|
}
|
|
34
35
|
return null;
|
|
35
36
|
}
|
|
@@ -39,7 +40,7 @@ function extractReturnedFunctionResult(node) {
|
|
|
39
40
|
const resultNode = node.childForFieldName('result');
|
|
40
41
|
if (resultNode?.type !== 'function_type') return null;
|
|
41
42
|
const innerResult = resultNode.childForFieldName('result');
|
|
42
|
-
return innerResult
|
|
43
|
+
return nodeTextWithoutComments(innerResult).trim() || null;
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
/**
|
|
@@ -51,7 +52,7 @@ function extractGoParams(paramsNode) {
|
|
|
51
52
|
// unknown signatures in JSON output (fix #238; the shared
|
|
52
53
|
// utils.extractParams already had this fix).
|
|
53
54
|
if (!paramsNode) return '...';
|
|
54
|
-
const text = paramsNode
|
|
55
|
+
const text = nodeTextWithoutComments(paramsNode);
|
|
55
56
|
return text.replace(/^\(|\)$/g, '').trim();
|
|
56
57
|
}
|
|
57
58
|
|
|
@@ -115,7 +116,7 @@ function _processFunction(node, functions, processedRanges, lines) {
|
|
|
115
116
|
indent,
|
|
116
117
|
modifiers: isExported ? ['export'] : [],
|
|
117
118
|
isFunctionVariable: true,
|
|
118
|
-
...(resultNode
|
|
119
|
+
...(resultNode && { returnType: nodeTextWithoutComments(resultNode).trim() || null }),
|
|
119
120
|
});
|
|
120
121
|
}
|
|
121
122
|
return true;
|
|
@@ -491,11 +492,11 @@ function extractInterfaceMembers(interfaceNode, codeOrLines) {
|
|
|
491
492
|
} else if (sub.type === 'parameter_list') {
|
|
492
493
|
hasParams = true;
|
|
493
494
|
if (!paramsText) {
|
|
494
|
-
paramsText = sub.
|
|
495
|
+
paramsText = nodeTextWithoutComments(sub).slice(1, -1); // strip parens
|
|
495
496
|
paramsNode = sub;
|
|
496
497
|
} else {
|
|
497
498
|
// Second parameter_list is the return type tuple
|
|
498
|
-
returnType = sub
|
|
499
|
+
returnType = nodeTextWithoutComments(sub);
|
|
499
500
|
}
|
|
500
501
|
}
|
|
501
502
|
}
|
|
@@ -514,7 +515,7 @@ function extractInterfaceMembers(interfaceNode, codeOrLines) {
|
|
|
514
515
|
for (let j = 0; j < child.namedChildCount; j++) {
|
|
515
516
|
const sub = child.namedChild(j);
|
|
516
517
|
if (returnTypeNodes.has(sub.type) && sub.text !== nameText) {
|
|
517
|
-
returnType = sub
|
|
518
|
+
returnType = nodeTextWithoutComments(sub);
|
|
518
519
|
}
|
|
519
520
|
}
|
|
520
521
|
}
|
package/languages/java.js
CHANGED
|
@@ -10,6 +10,7 @@ const { ReceiverTypeMap, typeOrigin } = require('./type-evidence');
|
|
|
10
10
|
|
|
11
11
|
const {
|
|
12
12
|
traverseTree,
|
|
13
|
+
nodeTextWithoutComments,
|
|
13
14
|
traverseTreeCached,
|
|
14
15
|
nodeToLocation,
|
|
15
16
|
parseStructuredParams,
|
|
@@ -32,7 +33,7 @@ function extractJavaParams(paramsNode) {
|
|
|
32
33
|
// unknown signatures in JSON output (fix #241; go/rust got this in #238,
|
|
33
34
|
// the shared utils.extractParams already had it).
|
|
34
35
|
if (!paramsNode) return '...';
|
|
35
|
-
const text = paramsNode
|
|
36
|
+
const text = nodeTextWithoutComments(paramsNode);
|
|
36
37
|
let params = text.replace(/^\(|\)$/g, '').trim();
|
|
37
38
|
return params;
|
|
38
39
|
}
|
|
@@ -201,7 +202,7 @@ function stripJavaString(text) {
|
|
|
201
202
|
function extractReturnType(node) {
|
|
202
203
|
const typeNode = node.childForFieldName('type');
|
|
203
204
|
if (typeNode) {
|
|
204
|
-
return typeNode
|
|
205
|
+
return nodeTextWithoutComments(typeNode);
|
|
205
206
|
}
|
|
206
207
|
return null;
|
|
207
208
|
}
|
package/languages/javascript.js
CHANGED
|
@@ -10,6 +10,7 @@ const { ReceiverTypeMap, typeOrigin } = require('./type-evidence');
|
|
|
10
10
|
|
|
11
11
|
const {
|
|
12
12
|
traverseTree,
|
|
13
|
+
nodeTextWithoutComments,
|
|
13
14
|
traverseTreeCached,
|
|
14
15
|
nodeToLocation,
|
|
15
16
|
extractParams,
|
|
@@ -34,7 +35,7 @@ function parseTree(parser, code) {
|
|
|
34
35
|
function extractReturnType(node) {
|
|
35
36
|
const returnTypeNode = node.childForFieldName('return_type');
|
|
36
37
|
if (returnTypeNode) {
|
|
37
|
-
let text = returnTypeNode.
|
|
38
|
+
let text = nodeTextWithoutComments(returnTypeNode).trim();
|
|
38
39
|
if (text.startsWith(':')) {
|
|
39
40
|
text = text.slice(1).trim();
|
|
40
41
|
}
|
package/languages/python.js
CHANGED
|
@@ -10,6 +10,7 @@ const { ReceiverTypeMap, typeOrigin } = require('./type-evidence');
|
|
|
10
10
|
|
|
11
11
|
const {
|
|
12
12
|
traverseTree,
|
|
13
|
+
nodeTextWithoutComments,
|
|
13
14
|
traverseTreeCached,
|
|
14
15
|
nodeToLocation,
|
|
15
16
|
parseStructuredParams,
|
|
@@ -32,7 +33,7 @@ function parseTree(parser, code) {
|
|
|
32
33
|
function extractReturnType(node) {
|
|
33
34
|
const returnTypeNode = node.childForFieldName('return_type');
|
|
34
35
|
if (returnTypeNode) {
|
|
35
|
-
let text = returnTypeNode.
|
|
36
|
+
let text = nodeTextWithoutComments(returnTypeNode).trim();
|
|
36
37
|
if (text.startsWith('->')) {
|
|
37
38
|
text = text.slice(2).trim();
|
|
38
39
|
}
|
|
@@ -129,7 +130,7 @@ function extractPythonParams(paramsNode) {
|
|
|
129
130
|
// unknown signatures in JSON output (fix #241; go/rust got this in #238,
|
|
130
131
|
// the shared utils.extractParams already had it).
|
|
131
132
|
if (!paramsNode) return '...';
|
|
132
|
-
const text = paramsNode
|
|
133
|
+
const text = nodeTextWithoutComments(paramsNode);
|
|
133
134
|
let params = text.replace(/^\(|\)$/g, '').trim();
|
|
134
135
|
return params;
|
|
135
136
|
}
|
package/languages/rust.js
CHANGED
|
@@ -10,6 +10,7 @@ const { ReceiverTypeMap, typeOrigin } = require('./type-evidence');
|
|
|
10
10
|
|
|
11
11
|
const {
|
|
12
12
|
traverseTree,
|
|
13
|
+
nodeTextWithoutComments,
|
|
13
14
|
traverseTreeCached,
|
|
14
15
|
nodeToLocation,
|
|
15
16
|
parseStructuredParams,
|
|
@@ -128,7 +129,7 @@ function declarationTrees(code, parser) {
|
|
|
128
129
|
function extractReturnType(node) {
|
|
129
130
|
const returnTypeNode = node.childForFieldName('return_type');
|
|
130
131
|
if (returnTypeNode) {
|
|
131
|
-
let text = returnTypeNode.
|
|
132
|
+
let text = nodeTextWithoutComments(returnTypeNode).trim();
|
|
132
133
|
if (text.startsWith('->')) {
|
|
133
134
|
text = text.slice(2).trim();
|
|
134
135
|
}
|
|
@@ -198,7 +199,7 @@ function extractRustParams(paramsNode) {
|
|
|
198
199
|
// unknown signatures in JSON output (fix #238; the shared
|
|
199
200
|
// utils.extractParams already had this fix).
|
|
200
201
|
if (!paramsNode) return '...';
|
|
201
|
-
const text = paramsNode
|
|
202
|
+
const text = nodeTextWithoutComments(paramsNode);
|
|
202
203
|
return text.replace(/^\(|\)$/g, '').trim();
|
|
203
204
|
}
|
|
204
205
|
|
package/languages/utils.js
CHANGED
|
@@ -54,11 +54,32 @@ function extractParams(paramsNode) {
|
|
|
54
54
|
// functions that actually take zero arguments. Empty → '' so callers can
|
|
55
55
|
// render `main()` cleanly.
|
|
56
56
|
if (!paramsNode) return '...';
|
|
57
|
-
const text = paramsNode
|
|
57
|
+
const text = nodeTextWithoutComments(paramsNode);
|
|
58
58
|
const stripped = text.replace(/^\(|\)$/g, '').trim();
|
|
59
59
|
return stripped; // '' for empty params, '...' only when paramsNode missing
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Signature text without AST comment nodes. Keep source offsets/line breaks
|
|
64
|
+
* intact for declarator slicing, and preserve strings (including forward
|
|
65
|
+
* annotations and defaults containing comment-like text) verbatim.
|
|
66
|
+
*/
|
|
67
|
+
function nodeTextWithoutComments(node) {
|
|
68
|
+
if (!node) return '';
|
|
69
|
+
const text = node.text;
|
|
70
|
+
const parts = [];
|
|
71
|
+
let offset = 0;
|
|
72
|
+
traverseTree(node, child => {
|
|
73
|
+
if (child.type !== 'comment' && !child.type.endsWith('_comment')) return true;
|
|
74
|
+
const start = child.startIndex - node.startIndex;
|
|
75
|
+
const end = child.endIndex - node.startIndex;
|
|
76
|
+
parts.push(text.slice(offset, start), text.slice(start, end).replace(/[^\r\n]/g, ' '));
|
|
77
|
+
offset = end;
|
|
78
|
+
return false;
|
|
79
|
+
});
|
|
80
|
+
return parts.length ? parts.join('') + text.slice(offset) : text;
|
|
81
|
+
}
|
|
82
|
+
|
|
62
83
|
/**
|
|
63
84
|
* Parse parameters into structured format
|
|
64
85
|
* @param {object} paramsNode - Tree-sitter parameters node
|
|
@@ -140,7 +161,7 @@ function parseStructuredParams(paramsNode, language) {
|
|
|
140
161
|
|
|
141
162
|
function parseJSParam(param, info) {
|
|
142
163
|
if (param.type === 'identifier') {
|
|
143
|
-
info.name = param
|
|
164
|
+
info.name = nodeTextWithoutComments(param);
|
|
144
165
|
} else if (param.type === 'required_parameter' || param.type === 'optional_parameter') {
|
|
145
166
|
const patternNode = param.childForFieldName('pattern');
|
|
146
167
|
const typeNode = param.childForFieldName('type');
|
|
@@ -148,18 +169,18 @@ function parseJSParam(param, info) {
|
|
|
148
169
|
// Check if pattern is a rest_pattern (e.g., ...args inside required_parameter)
|
|
149
170
|
if (patternNode.type === 'rest_pattern') {
|
|
150
171
|
const innerName = patternNode.namedChild(0);
|
|
151
|
-
info.name = innerName ? innerName
|
|
172
|
+
info.name = innerName ? nodeTextWithoutComments(innerName) : nodeTextWithoutComments(patternNode).replace(/^\.\.\./, '');
|
|
152
173
|
info.rest = true;
|
|
153
174
|
} else {
|
|
154
|
-
info.name = patternNode
|
|
175
|
+
info.name = nodeTextWithoutComments(patternNode);
|
|
155
176
|
}
|
|
156
177
|
}
|
|
157
|
-
if (typeNode) info.type = typeNode.
|
|
178
|
+
if (typeNode) info.type = nodeTextWithoutComments(typeNode).replace(/^:\s*/, '');
|
|
158
179
|
if (param.type === 'optional_parameter') info.optional = true;
|
|
159
180
|
// Check for default value (e.g., priority: number = 1)
|
|
160
181
|
const valueNode = param.childForFieldName('value');
|
|
161
182
|
if (valueNode) {
|
|
162
|
-
info.default = valueNode
|
|
183
|
+
info.default = nodeTextWithoutComments(valueNode);
|
|
163
184
|
info.optional = true;
|
|
164
185
|
} else if (!info.rest) {
|
|
165
186
|
// Also check for bare number/string/etc. children as defaults.
|
|
@@ -170,14 +191,14 @@ function parseJSParam(param, info) {
|
|
|
170
191
|
// wrecking expectedArgs.min and the signature display).
|
|
171
192
|
const NON_DEFAULT_PARAM_CHILDREN = new Set([
|
|
172
193
|
'identifier', 'type_annotation', 'rest_pattern',
|
|
173
|
-
'accessibility_modifier', 'override_modifier', 'readonly', 'decorator',
|
|
194
|
+
'accessibility_modifier', 'override_modifier', 'readonly', 'decorator', 'comment',
|
|
174
195
|
]);
|
|
175
196
|
for (let i = 0; i < param.namedChildCount; i++) {
|
|
176
197
|
const child = param.namedChild(i);
|
|
177
198
|
if (child !== patternNode && child !== (typeNode && typeNode.parent === param ? typeNode : null) &&
|
|
178
199
|
!NON_DEFAULT_PARAM_CHILDREN.has(child.type)) {
|
|
179
200
|
// This is likely a default value node
|
|
180
|
-
info.default = child
|
|
201
|
+
info.default = nodeTextWithoutComments(child);
|
|
181
202
|
info.optional = true;
|
|
182
203
|
break;
|
|
183
204
|
}
|
|
@@ -186,27 +207,27 @@ function parseJSParam(param, info) {
|
|
|
186
207
|
} else if (param.type === 'rest_parameter' || param.type === 'rest_pattern') {
|
|
187
208
|
// rest_parameter = TypeScript, rest_pattern = JavaScript
|
|
188
209
|
const patternNode = param.childForFieldName('pattern') || param.namedChild(0);
|
|
189
|
-
if (patternNode) info.name = patternNode
|
|
210
|
+
if (patternNode) info.name = nodeTextWithoutComments(patternNode);
|
|
190
211
|
info.rest = true;
|
|
191
212
|
} else if (param.type === 'assignment_pattern') {
|
|
192
213
|
const leftNode = param.childForFieldName('left');
|
|
193
214
|
const rightNode = param.childForFieldName('right');
|
|
194
|
-
if (leftNode) info.name = leftNode
|
|
195
|
-
if (rightNode) info.default = rightNode
|
|
215
|
+
if (leftNode) info.name = nodeTextWithoutComments(leftNode);
|
|
216
|
+
if (rightNode) info.default = nodeTextWithoutComments(rightNode);
|
|
196
217
|
} else if (param.type === 'object_pattern' || param.type === 'array_pattern') {
|
|
197
218
|
// Destructured params: { name, value } or [a, b]
|
|
198
|
-
info.name = param
|
|
219
|
+
info.name = nodeTextWithoutComments(param);
|
|
199
220
|
}
|
|
200
221
|
}
|
|
201
222
|
|
|
202
223
|
function parsePythonParam(param, info) {
|
|
203
224
|
if (param.type === 'identifier') {
|
|
204
|
-
info.name = param
|
|
225
|
+
info.name = nodeTextWithoutComments(param);
|
|
205
226
|
} else if (param.type === 'typed_parameter') {
|
|
206
227
|
const nameNode = param.namedChild(0);
|
|
207
228
|
const typeNode = param.childForFieldName('type');
|
|
208
|
-
if (nameNode) info.name = nameNode
|
|
209
|
-
if (typeNode) info.type = typeNode
|
|
229
|
+
if (nameNode) info.name = nodeTextWithoutComments(nameNode);
|
|
230
|
+
if (typeNode) info.type = nodeTextWithoutComments(typeNode);
|
|
210
231
|
// Python wraps annotated splats in typed_parameter, with the actual
|
|
211
232
|
// `*args` / `**kwargs` node as its first named child. Treating those
|
|
212
233
|
// as ordinary required parameters makes every short call look broken.
|
|
@@ -218,12 +239,12 @@ function parsePythonParam(param, info) {
|
|
|
218
239
|
const nameNode = param.childForFieldName('name');
|
|
219
240
|
const valueNode = param.childForFieldName('value');
|
|
220
241
|
const typeNode = param.childForFieldName('type');
|
|
221
|
-
if (nameNode) info.name = nameNode
|
|
222
|
-
if (valueNode) info.default = valueNode
|
|
223
|
-
if (typeNode) info.type = typeNode
|
|
242
|
+
if (nameNode) info.name = nodeTextWithoutComments(nameNode);
|
|
243
|
+
if (valueNode) info.default = nodeTextWithoutComments(valueNode);
|
|
244
|
+
if (typeNode) info.type = nodeTextWithoutComments(typeNode);
|
|
224
245
|
info.optional = true;
|
|
225
246
|
} else if (param.type === 'list_splat_pattern' || param.type === 'dictionary_splat_pattern') {
|
|
226
|
-
info.name = param
|
|
247
|
+
info.name = nodeTextWithoutComments(param);
|
|
227
248
|
info.rest = true;
|
|
228
249
|
}
|
|
229
250
|
}
|
|
@@ -237,11 +258,11 @@ function parseGoParam(param, info) {
|
|
|
237
258
|
for (let i = 0; i < param.namedChildCount; i++) {
|
|
238
259
|
const child = param.namedChild(i);
|
|
239
260
|
if (child && child.type === 'identifier') {
|
|
240
|
-
names.push(child
|
|
261
|
+
names.push(nodeTextWithoutComments(child));
|
|
241
262
|
}
|
|
242
263
|
}
|
|
243
264
|
if (names.length > 0) info.name = names[0];
|
|
244
|
-
if (typeNode) info.type = typeNode
|
|
265
|
+
if (typeNode) info.type = nodeTextWithoutComments(typeNode);
|
|
245
266
|
// Interface method declarations commonly omit parameter names:
|
|
246
267
|
// `Match(*http.Request, *RouteMatch) bool`. These are still two real
|
|
247
268
|
// signature slots. Dropping them made verify/plan see zero arguments
|
|
@@ -249,7 +270,7 @@ function parseGoParam(param, info) {
|
|
|
249
270
|
// slot. Preserve the authored type as the display token while marking
|
|
250
271
|
// it unnamed so signature consumers can distinguish it from a name.
|
|
251
272
|
if (names.length === 0 && typeNode) {
|
|
252
|
-
info.name = typeNode
|
|
273
|
+
info.name = nodeTextWithoutComments(typeNode);
|
|
253
274
|
info.unnamed = true;
|
|
254
275
|
delete info.type;
|
|
255
276
|
}
|
|
@@ -261,9 +282,9 @@ function parseGoParam(param, info) {
|
|
|
261
282
|
// Go variadic: `args ...int`
|
|
262
283
|
const nameNode = param.childForFieldName('name');
|
|
263
284
|
const typeNode = param.childForFieldName('type');
|
|
264
|
-
if (nameNode) info.name = nameNode
|
|
285
|
+
if (nameNode) info.name = nodeTextWithoutComments(nameNode);
|
|
265
286
|
else info.name = '...';
|
|
266
|
-
if (typeNode) info.type = '...' + typeNode
|
|
287
|
+
if (typeNode) info.type = '...' + nodeTextWithoutComments(typeNode);
|
|
267
288
|
info.rest = true;
|
|
268
289
|
}
|
|
269
290
|
}
|
|
@@ -272,10 +293,10 @@ function parseRustParam(param, info) {
|
|
|
272
293
|
if (param.type === 'parameter') {
|
|
273
294
|
const patternNode = param.childForFieldName('pattern');
|
|
274
295
|
const typeNode = param.childForFieldName('type');
|
|
275
|
-
if (patternNode) info.name = patternNode
|
|
276
|
-
if (typeNode) info.type = typeNode
|
|
296
|
+
if (patternNode) info.name = nodeTextWithoutComments(patternNode);
|
|
297
|
+
if (typeNode) info.type = nodeTextWithoutComments(typeNode);
|
|
277
298
|
} else if (param.type === 'self_parameter') {
|
|
278
|
-
info.name = param
|
|
299
|
+
info.name = nodeTextWithoutComments(param);
|
|
279
300
|
}
|
|
280
301
|
}
|
|
281
302
|
|
|
@@ -294,8 +315,8 @@ function parseJavaParam(param, info) {
|
|
|
294
315
|
}
|
|
295
316
|
}
|
|
296
317
|
}
|
|
297
|
-
if (nameNode) info.name = nameNode
|
|
298
|
-
if (typeNode) info.type = typeNode
|
|
318
|
+
if (nameNode) info.name = nodeTextWithoutComments(nameNode);
|
|
319
|
+
if (typeNode) info.type = nodeTextWithoutComments(typeNode);
|
|
299
320
|
if (param.type === 'spread_parameter') info.rest = true;
|
|
300
321
|
}
|
|
301
322
|
}
|
|
@@ -1051,6 +1072,7 @@ function sameNode(a, b) {
|
|
|
1051
1072
|
}
|
|
1052
1073
|
|
|
1053
1074
|
module.exports = {
|
|
1075
|
+
nodeTextWithoutComments,
|
|
1054
1076
|
sameNode,
|
|
1055
1077
|
traverseTree,
|
|
1056
1078
|
traverseTreeCached,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ucn",
|
|
3
|
-
"version": "5.3.
|
|
3
|
+
"version": "5.3.8",
|
|
4
4
|
"mcpName": "io.github.mleoca/ucn",
|
|
5
5
|
"description": "Auditable AST code intelligence for AI agents: 18 task-oriented commands through one MCP tool, CLI, or agent skill. Supports JS/TS, Python, Go, Rust, Java, C, C++, C#, and HTML.",
|
|
6
6
|
"main": "index.js",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
12
|
"version": "node scripts/sync-server-version.js && git add server.json",
|
|
13
|
-
"test": "node --test test/audit-5.3.6.test.js test/evidence-provenance.test.js test/provenance-unit.test.js test/shell-output.test.js test/parser-unit.test.js test/integration.test.js test/cache.test.js test/formatter.test.js test/interactive.test.js test/feature.test.js test/regression-js.test.js test/regression-py.test.js test/regression-go.test.js test/regression-java.test.js test/regression-rust.test.js test/regression-c-family.test.js test/regression-cross.test.js test/regression-mcp.test.js test/mcp-protocol.test.js test/mcp-sdk-compat.test.js test/dependency-security.test.js test/regression-parser.test.js test/regression-commands.test.js test/regression-fixes.test.js test/regression-bugfixes.test.js test/release-surface-regressions.test.js test/prerelease-audit.test.js test/release-readiness-audit.test.js test/cross-language.test.js test/accuracy.test.js test/command-coverage.test.js test/perf-optimizations.test.js test/performance-gate-policy.test.js test/oracle-gate-policy.test.js test/outcome-policy.test.js test/consistency-eval.test.js test/agent-public-surface-benchmark.test.js test/command-contracts.test.js test/language-adapter.test.js test/systematic-test.js test/mcp-edge-cases.js test/conservation.test.js test/parity-test.js test/trust-matrix.test.js",
|
|
13
|
+
"test": "node --test test/audit-5.3.7.test.js test/audit-5.3.6.test.js test/evidence-provenance.test.js test/provenance-unit.test.js test/shell-output.test.js test/parser-unit.test.js test/integration.test.js test/cache.test.js test/formatter.test.js test/interactive.test.js test/feature.test.js test/regression-js.test.js test/regression-py.test.js test/regression-go.test.js test/regression-java.test.js test/regression-rust.test.js test/regression-c-family.test.js test/regression-cross.test.js test/regression-mcp.test.js test/mcp-protocol.test.js test/mcp-sdk-compat.test.js test/dependency-security.test.js test/regression-parser.test.js test/regression-commands.test.js test/regression-fixes.test.js test/regression-bugfixes.test.js test/release-surface-regressions.test.js test/prerelease-audit.test.js test/release-readiness-audit.test.js test/cross-language.test.js test/accuracy.test.js test/command-coverage.test.js test/perf-optimizations.test.js test/performance-gate-policy.test.js test/oracle-gate-policy.test.js test/outcome-policy.test.js test/consistency-eval.test.js test/agent-public-surface-benchmark.test.js test/command-contracts.test.js test/language-adapter.test.js test/systematic-test.js test/mcp-edge-cases.js test/conservation.test.js test/parity-test.js test/trust-matrix.test.js",
|
|
14
14
|
"benchmark:agent": "node test/agent-public-surface-benchmark.js",
|
|
15
15
|
"benchmark:agent:gate": "node test/agent-public-surface-benchmark.js --gate",
|
|
16
16
|
"benchmark:agent:legacy": "node test/agent-understanding-benchmark.js",
|