ucn 5.5.3 → 5.6.1
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 +37 -12
- package/.claude/skills/ucn/references/commands.md +18 -14
- package/.claude/skills/ucn/references/resolution-rules.md +4 -0
- package/.claude/skills/ucn/references/trust-contract.md +4 -4
- package/CHANGELOG.md +485 -0
- package/README.md +66 -29
- package/cli/flags.js +184 -0
- package/cli/index.js +137 -324
- package/core/account.js +44 -87
- package/core/analysis.js +274 -108
- package/core/ast-analysis.js +2 -0
- package/core/bridge.js +14 -0
- package/core/cache.js +103 -16
- package/core/call-fit.js +130 -0
- package/core/callers.js +353 -71
- package/core/capture.js +363 -0
- package/core/change-kinds.js +1107 -0
- package/core/check.js +1150 -139
- package/core/command-contracts.js +8 -7
- package/core/constructed-class.js +1195 -0
- package/core/contract-membership.js +142 -0
- package/core/deadcode.js +455 -106
- package/core/deletion.js +421 -0
- package/core/discovery.js +104 -9
- package/core/entrypoint-toml.js +110 -0
- package/core/entrypoints.js +12 -6
- package/core/evidence-bands.js +68 -0
- package/core/evidence-bands.json +44 -0
- package/core/execute.js +172 -161
- package/core/hot-summary.js +241 -0
- package/core/index-ir.js +5 -3
- package/core/ir.js +6 -2
- package/core/member-signatures.js +789 -0
- package/core/outgoing-check.js +116 -0
- package/core/output/analysis-ext.js +20 -10
- package/core/output/analysis.js +98 -60
- package/core/output/check.js +101 -37
- package/core/output/doctor.js +34 -1
- package/core/output/find.js +2 -1
- package/core/output/lines.js +63 -3
- package/core/output/public.js +29 -11
- package/core/output/refactoring.js +32 -0
- package/core/output/reporting.js +19 -2
- package/core/output/search.js +14 -2
- package/core/output/tracing.js +36 -8
- package/core/output-budget.js +146 -61
- package/core/parallel-build.js +3 -3
- package/core/project.js +106 -23
- package/core/provenance-type.js +12 -5
- package/core/public-command.js +35 -9
- package/core/python-binding-identity.js +707 -0
- package/core/python-modules.js +6 -0
- package/core/reflection.js +248 -31
- package/core/registry.js +623 -72
- package/core/reporting.js +69 -34
- package/core/review-items.js +75 -0
- package/core/runtime-entrypoints.js +159 -0
- package/core/search.js +148 -164
- package/core/shared.js +0 -45
- package/core/snapshot.js +350 -0
- package/core/source-universe.js +707 -0
- package/core/test-units.js +3698 -0
- package/core/tracing.js +270 -658
- package/core/verify.js +63 -22
- package/languages/adapter.js +8 -3
- package/languages/c-family.js +12 -0
- package/languages/csharp.js +634 -0
- package/languages/go.js +120 -0
- package/languages/index.js +466 -4
- package/languages/java.js +712 -0
- package/languages/javascript.js +1498 -6
- package/languages/lexical-scope.js +68 -0
- package/languages/python.js +1708 -3
- package/languages/rust.js +209 -104
- package/languages/static-js.js +88 -0
- package/mcp/server.js +44 -110
- package/package.json +11 -5
package/languages/go.js
CHANGED
|
@@ -719,6 +719,9 @@ function extractInterfaceMembers(interfaceNode, codeOrLines) {
|
|
|
719
719
|
endLine,
|
|
720
720
|
sourceSpan,
|
|
721
721
|
memberType: 'method',
|
|
722
|
+
// An interface method element is a requirement of
|
|
723
|
+
// every type used as the interface.
|
|
724
|
+
requiredMember: true,
|
|
722
725
|
...(paramsText !== null && { params: paramsText }),
|
|
723
726
|
...(paramsNode && { paramsStructured: parseStructuredParams(paramsNode, 'go') }),
|
|
724
727
|
...(returnType && { returnType })
|
|
@@ -3841,6 +3844,121 @@ function selectorReceiverIsLocal(tree, line, column) {
|
|
|
3841
3844
|
return !!scope && typeof scope !== 'string';
|
|
3842
3845
|
}
|
|
3843
3846
|
|
|
3847
|
+
|
|
3848
|
+
/**
|
|
3849
|
+
* The tests a Go file declares, as "go test" finds them: in a _test.go
|
|
3850
|
+
* file, a function (never a method) named Test or Fuzz followed by nothing
|
|
3851
|
+
* or by a rune that is not lower case; TestMain is the harness, not a
|
|
3852
|
+
* test; an Example runs only when its body ends in an output comment. A
|
|
3853
|
+
* Benchmark runs only when asked for, so it is not one of the tests.
|
|
3854
|
+
*/
|
|
3855
|
+
/**
|
|
3856
|
+
* Does a Go test file hold a TestMain that may keep the package's tests
|
|
3857
|
+
* from running. A TestMain decides nothing when the first statement of
|
|
3858
|
+
* its body that can end it or run the tests is a plain statement holding
|
|
3859
|
+
* `m.Run()` (`os.Exit(m.Run())`, `code := m.Run()`): nothing before it
|
|
3860
|
+
* returns or exits. One that exits or returns in a branch first, never
|
|
3861
|
+
* calls Run, or hands `m` to something else, decides.
|
|
3862
|
+
*/
|
|
3863
|
+
function testGate(fileEntry, helpers) {
|
|
3864
|
+
if (!/_test\.go$/.test(fileEntry.relativePath)) return false;
|
|
3865
|
+
if (!(fileEntry.symbols || []).some(symbol => symbol.name === 'TestMain' && !symbol.className)) return false;
|
|
3866
|
+
const tree = helpers.tree();
|
|
3867
|
+
if (!tree) return true;
|
|
3868
|
+
const main = tree.rootNode.namedChildren.find(node => node.type === 'function_declaration' &&
|
|
3869
|
+
node.childForFieldName('name')?.text === 'TestMain');
|
|
3870
|
+
if (!main) return true;
|
|
3871
|
+
const body = main.childForFieldName('body');
|
|
3872
|
+
const list = main.childForFieldName('parameters');
|
|
3873
|
+
const first = list && list.namedChildren[0];
|
|
3874
|
+
const handle = first && first.childForFieldName('name') ? first.childForFieldName('name').text : null;
|
|
3875
|
+
if (!body || !handle) return true;
|
|
3876
|
+
const calls = (node, test) => (node.type === 'call_expression' && test(node)) || node.namedChildren.some(child => calls(child, test));
|
|
3877
|
+
const runs = node => {
|
|
3878
|
+
const callee = node.childForFieldName('function');
|
|
3879
|
+
return !!callee && callee.type === 'selector_expression' && callee.childForFieldName('operand')?.text === handle &&
|
|
3880
|
+
callee.childForFieldName('field')?.text === 'Run';
|
|
3881
|
+
};
|
|
3882
|
+
const ends = node => {
|
|
3883
|
+
const callee = node.childForFieldName('function');
|
|
3884
|
+
const text = callee ? callee.text : '';
|
|
3885
|
+
const args = node.childForFieldName('arguments');
|
|
3886
|
+
return /^(os\.Exit|panic|runtime\.Goexit|log\.(Fatal|Fatalf|Fatalln|Panic|Panicf|Panicln))$/.test(text) ||
|
|
3887
|
+
(args ? args.namedChildren : []).some(argument => argument.type === 'identifier' && argument.text === handle);
|
|
3888
|
+
};
|
|
3889
|
+
const returns = node => node.type === 'return_statement' || (node.type !== 'func_literal' && node.namedChildren.some(returns));
|
|
3890
|
+
const statements = body.namedChildren.filter(node => node.type !== 'comment');
|
|
3891
|
+
for (const statement of statements.length === 1 && statements[0].type === 'statement_list' ? statements[0].namedChildren : statements) {
|
|
3892
|
+
if (statement.type === 'comment') continue;
|
|
3893
|
+
const running = calls(statement, runs);
|
|
3894
|
+
if (!running && !calls(statement, ends) && !returns(statement)) continue;
|
|
3895
|
+
const plain = ['expression_statement', 'short_var_declaration', 'assignment_statement', 'var_declaration'].includes(statement.type);
|
|
3896
|
+
return !(running && plain && !calls(statement, node => !runs(node) && ends(node) && !/^os\.Exit$/.test(node.childForFieldName('function')?.text || '')));
|
|
3897
|
+
}
|
|
3898
|
+
return true;
|
|
3899
|
+
}
|
|
3900
|
+
|
|
3901
|
+
/**
|
|
3902
|
+
* Does an Example function end in an output comment? go runs an example
|
|
3903
|
+
* only then. The comment is a comment of the function's body (vet insists
|
|
3904
|
+
* it is the last one): text inside a string is not one.
|
|
3905
|
+
*/
|
|
3906
|
+
function exampleHasOutput(tree, symbol) {
|
|
3907
|
+
if (!tree) return false;
|
|
3908
|
+
const line = (symbol.nameLine || symbol.startLine) - 1;
|
|
3909
|
+
const declaration = tree.rootNode.namedChildren.find(node => node.type === 'function_declaration' &&
|
|
3910
|
+
node.startPosition.row <= line && node.endPosition.row >= line &&
|
|
3911
|
+
node.childForFieldName('name')?.text === symbol.name);
|
|
3912
|
+
const body = declaration && declaration.childForFieldName('body');
|
|
3913
|
+
if (!body) return false;
|
|
3914
|
+
let found = false;
|
|
3915
|
+
const scan = node => {
|
|
3916
|
+
if (found) return;
|
|
3917
|
+
if (node.type === 'comment') {
|
|
3918
|
+
const text = node.text.replace(/^\/\/|^\/\*/, '');
|
|
3919
|
+
if (/^\s*(unordered )?output:/i.test(text)) found = true;
|
|
3920
|
+
return;
|
|
3921
|
+
}
|
|
3922
|
+
for (const child of node.namedChildren) scan(child);
|
|
3923
|
+
};
|
|
3924
|
+
scan(body);
|
|
3925
|
+
return found;
|
|
3926
|
+
}
|
|
3927
|
+
|
|
3928
|
+
const GO_PLATFORM_WORDS = new Set(['aix', 'android', 'darwin', 'dragonfly', 'freebsd', 'hurd', 'illumos', 'ios',
|
|
3929
|
+
'js', 'linux', 'nacl', 'netbsd', 'openbsd', 'plan9', 'solaris', 'wasip1', 'windows', 'zos',
|
|
3930
|
+
'386', 'amd64', 'amd64p32', 'arm', 'armbe', 'arm64', 'arm64be', 'loong64', 'mips', 'mipsle', 'mips64',
|
|
3931
|
+
'mips64le', 'mips64p32', 'mips64p32le', 'ppc', 'ppc64', 'ppc64le', 'riscv', 'riscv64', 's390', 's390x',
|
|
3932
|
+
'sparc', 'sparc64', 'wasm']);
|
|
3933
|
+
|
|
3934
|
+
function testUnits(fileEntry, helpers) {
|
|
3935
|
+
if (!/_test\.go$/.test(fileEntry.relativePath)) return { units: [], inheriting: false, complete: true };
|
|
3936
|
+
// name_GOOS_test.go, name_GOARCH_test.go, name_GOOS_GOARCH_test.go
|
|
3937
|
+
const words = fileEntry.relativePath.split('/').pop().replace(/_test\.go$/, '').split('_').slice(1);
|
|
3938
|
+
const platform = words.length > 0 && GO_PLATFORM_WORDS.has(words[words.length - 1]);
|
|
3939
|
+
const units = [];
|
|
3940
|
+
for (const symbol of fileEntry.symbols || []) {
|
|
3941
|
+
if (symbol.type !== 'function' || symbol.isMethod || symbol.receiver || symbol.className) continue;
|
|
3942
|
+
const match = /^(Test|Fuzz|Example)([\s\S]*)$/.exec(symbol.name);
|
|
3943
|
+
if (!match) continue;
|
|
3944
|
+
const first = match[2] ? String.fromCodePoint(match[2].codePointAt(0)) : '';
|
|
3945
|
+
if (first && first.toLowerCase() === first && first.toUpperCase() !== first) continue;
|
|
3946
|
+
if (symbol.name === 'TestMain' && /testing\s*\.\s*M\b/.test(String(symbol.params || ''))) continue;
|
|
3947
|
+
if (helpers.enclosing(symbol).length > 0) continue;
|
|
3948
|
+
const form = match[1].toLowerCase();
|
|
3949
|
+
if (form === 'example' && !exampleHasOutput(helpers.tree(), symbol)) continue;
|
|
3950
|
+
units.push({
|
|
3951
|
+
kind: 'test', name: symbol.name, suite: [], form,
|
|
3952
|
+
// A file under a build constraint is compiled only sometimes.
|
|
3953
|
+
...((fileEntry.buildConstraint || platform) && { conditional: true, notInEveryBuild: true }),
|
|
3954
|
+
line: symbol.nameLine || symbol.startLine, startLine: symbol.startLine,
|
|
3955
|
+
endLine: symbol.endLine ?? symbol.startLine, sourceSpan: symbol.sourceSpan,
|
|
3956
|
+
});
|
|
3957
|
+
}
|
|
3958
|
+
// Go has no test that is not a declaration: no unit, no test.
|
|
3959
|
+
return { units, inheriting: false, complete: true };
|
|
3960
|
+
}
|
|
3961
|
+
|
|
3844
3962
|
/**
|
|
3845
3963
|
* Classify a Go symbol as a runtime entry point of a specific kind.
|
|
3846
3964
|
* Returns 'test' | 'main' | null.
|
|
@@ -3902,5 +4020,7 @@ module.exports = {
|
|
|
3902
4020
|
isPlatformConcreteType,
|
|
3903
4021
|
isEntryPoint,
|
|
3904
4022
|
getEntryPointKind,
|
|
4023
|
+
testUnits,
|
|
4024
|
+
testGate,
|
|
3905
4025
|
parse
|
|
3906
4026
|
};
|