ucn 5.4.2 → 5.5.0
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 +58 -236
- package/.claude/skills/ucn/references/commands.md +144 -68
- package/.claude/skills/ucn/references/resolution-rules.md +171 -0
- package/.claude/skills/ucn/references/trust-contract.md +27 -33
- package/README.md +18 -12
- package/cli/index.js +40 -14
- package/core/accessors.js +118 -3
- package/core/account.js +30 -0
- package/core/analysis.js +508 -264
- package/core/ast-analysis.js +76 -117
- package/core/async-producers.js +1729 -0
- package/core/bridge.js +1877 -268
- package/core/brief.js +6 -0
- package/core/build-worker.js +78 -14
- package/core/cache.js +522 -31
- package/core/callers.js +12053 -1047
- package/core/check.js +30 -38
- package/core/class-identity.js +1028 -0
- package/core/command-contracts.js +6 -6
- package/core/configuration-items.js +86 -0
- package/core/contract-membership.js +2262 -0
- package/core/cpp-scope.js +532 -0
- package/core/deadcode.js +171 -49
- package/core/decorator-binding.js +260 -0
- package/core/discovery.js +281 -164
- package/core/errors.js +52 -0
- package/core/execute.js +21 -9
- package/core/external-macros.js +326 -0
- package/core/graph-build.js +277 -8
- package/core/graph.js +35 -2
- package/core/ignore-rules.js +472 -0
- package/core/imports.js +504 -42
- package/core/index-ir.js +94 -11
- package/core/inprocess-clients.js +636 -0
- package/core/ir.js +87 -8
- package/core/macro-expansion.js +1416 -0
- package/core/output/analysis.js +18 -2
- package/core/output/brief.js +3 -0
- package/core/output/doctor.js +4 -0
- package/core/output/endpoints.js +15 -6
- package/core/output/find.js +7 -2
- package/core/output/refactoring.js +121 -14
- package/core/output/reporting.js +31 -3
- package/core/output/shared.js +14 -0
- package/core/output-budget.js +305 -122
- package/core/parallel-build.js +124 -47
- package/core/parser.js +3 -2
- package/core/project.js +457 -60
- package/core/provenance-facts.js +156 -12
- package/core/provenance-overload.js +30 -3
- package/core/provenance.js +104 -30
- package/core/pytest-fixtures.js +434 -0
- package/core/python-fixture-flow.js +17 -3
- package/core/python-modules.js +173 -0
- package/core/reflection.js +877 -0
- package/core/registry.js +1 -1
- package/core/reporting.js +234 -52
- package/core/request-helpers.js +561 -0
- package/core/route-graph.js +1922 -0
- package/core/route-lists.js +2414 -0
- package/core/rust-macro-expansion.js +2113 -0
- package/core/rust-modules.js +185 -0
- package/core/search.js +121 -29
- package/core/shared.js +19 -1
- package/core/test-name-bindings.js +256 -0
- package/core/type-denotation.js +1161 -0
- package/core/type-references.js +3229 -0
- package/core/verify.js +3360 -672
- package/languages/adapter.js +31 -5
- package/languages/c-family.js +4061 -212
- package/languages/c-macro-invocations.js +380 -0
- package/languages/c-preprocessor.js +254 -0
- package/languages/csharp.js +1406 -49
- package/languages/go.js +1243 -70
- package/languages/index.js +728 -6
- package/languages/java.js +473 -61
- package/languages/javascript.js +1270 -197
- package/languages/lexical-scope.js +1280 -0
- package/languages/python.js +1557 -72
- package/languages/rust-macro-rules.js +1020 -0
- package/languages/rust-value-flow.js +416 -0
- package/languages/rust.js +2237 -115
- package/languages/utils.js +298 -4
- package/mcp/server.js +22 -8
- package/package.json +1 -1
package/languages/index.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const path = require('path');
|
|
8
|
+
const { UcnError } = require('../core/errors');
|
|
8
9
|
const { createLanguageAdapter } = require('./adapter');
|
|
9
10
|
|
|
10
11
|
// Lazy-loaded tree-sitter
|
|
@@ -15,8 +16,106 @@ const parsers = {};
|
|
|
15
16
|
const adapters = {};
|
|
16
17
|
|
|
17
18
|
// Shared trait presets for languages with the same type-system characteristics
|
|
19
|
+
/**
|
|
20
|
+
* Build a `languageProtocolMember` predicate from rules (fix #360, #363).
|
|
21
|
+
* A rule names members the language runtime or compiler invokes BY NAME:
|
|
22
|
+
* { pattern | names, member?: true, owners?: [bare type names] }
|
|
23
|
+
* `member` restricts it to class members; `owners` restricts it to classes
|
|
24
|
+
* whose heritage reaches one of those out-of-project types (Enum hooks on
|
|
25
|
+
* Enum subclasses only). The predicate takes (name, ctx) with ctx
|
|
26
|
+
* { isMember, derivesFrom(ownerNames) }; without derivesFrom an owner-gated
|
|
27
|
+
* rule never matches.
|
|
28
|
+
*/
|
|
29
|
+
function protocolMembers(rules) {
|
|
30
|
+
return (name, ctx = {}) => {
|
|
31
|
+
let possible = false;
|
|
32
|
+
for (const rule of rules) {
|
|
33
|
+
const named = rule.pattern ? rule.pattern.test(name) : rule.names.includes(name);
|
|
34
|
+
if (!named) continue;
|
|
35
|
+
if (rule.member && ctx.isMember === false) continue;
|
|
36
|
+
// Owner kinds the runtime exempts from the protocol (fix #392:
|
|
37
|
+
// Java enum constants serialize by name and ignore the callbacks).
|
|
38
|
+
if (rule.exceptOwnerKinds && typeof ctx.ownerKinds === 'function' &&
|
|
39
|
+
ctx.ownerKinds().some(kind => rule.exceptOwnerKinds.includes(kind))) continue;
|
|
40
|
+
if (!rule.owners) return 'definite';
|
|
41
|
+
if (typeof ctx.derivesFrom === 'function' && ctx.derivesFrom(rule.owners)) return 'definite';
|
|
42
|
+
// An owner whose heritage leaves the project through another
|
|
43
|
+
// out-of-project supertype may still derive from the contract
|
|
44
|
+
// (fix #389: `extends IOException` is Serializable through the
|
|
45
|
+
// JDK): possible, when the member has the protocol's shape.
|
|
46
|
+
const via = rule.possibleViaExternal;
|
|
47
|
+
if (via && typeof ctx.externalSupertypes === 'function' &&
|
|
48
|
+
ctx.externalSupertypes().some(type => !via.except.includes(type)) &&
|
|
49
|
+
(!via.shape || via.shape(name, ctx))) possible = true;
|
|
50
|
+
}
|
|
51
|
+
return possible ? 'possible' : false;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Build a `languageProtocolType` predicate from rules (fix #380): types the
|
|
57
|
+
* compiler references by their full name when a language feature appears
|
|
58
|
+
* (C# `init` lowers to `System.Runtime.CompilerServices.IsExternalInit`).
|
|
59
|
+
* A project declaration of such a type in that namespace is used exactly
|
|
60
|
+
* when the project uses the feature. `ctx.hasFeature(name)` answers from the
|
|
61
|
+
* parser's per-file `languageFeatures`.
|
|
62
|
+
*/
|
|
63
|
+
function protocolTypes(rules) {
|
|
64
|
+
return (name, ctx = {}) => rules.some(rule => rule.names.includes(name) &&
|
|
65
|
+
(ctx.namespace || '') === rule.namespace &&
|
|
66
|
+
typeof ctx.hasFeature === 'function' && rule.features.some(feature => ctx.hasFeature(feature)));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The shape the Java serialization runtime looks for (fix #389): instance
|
|
71
|
+
* methods `writeObject(ObjectOutputStream)` / `readObject(ObjectInputStream)`,
|
|
72
|
+
* `readResolve()` / `writeReplace()` / `readObjectNoData()` without
|
|
73
|
+
* parameters, static fields `serialVersionUID` and `serialPersistentFields`.
|
|
74
|
+
*/
|
|
75
|
+
function javaSerializationShape(name, ctx) {
|
|
76
|
+
const modifiers = ctx.modifiers || [];
|
|
77
|
+
if (name === 'serialVersionUID' || name === 'serialPersistentFields') {
|
|
78
|
+
return ctx.isField === true && modifiers.includes('static');
|
|
79
|
+
}
|
|
80
|
+
if (ctx.isField || modifiers.includes('static')) return false;
|
|
81
|
+
const params = ctx.paramTypes;
|
|
82
|
+
if (!Array.isArray(params)) return true;
|
|
83
|
+
if (name === 'writeObject' || name === 'readObject') {
|
|
84
|
+
const stream = name === 'writeObject' ? 'ObjectOutputStream' : 'ObjectInputStream';
|
|
85
|
+
return params.length === 1 && (!params[0] || params[0] === stream);
|
|
86
|
+
}
|
|
87
|
+
return params.length === 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// java.lang types that do not implement java.io.Serializable (JDK API):
|
|
91
|
+
// an unqualified supertype spelled like one of these, and not imported
|
|
92
|
+
// from elsewhere, carries no serialization contract.
|
|
93
|
+
const JAVA_LANG_NOT_SERIALIZABLE = ['Object', 'Runnable', 'AutoCloseable', 'Comparable', 'Iterable',
|
|
94
|
+
'CharSequence', 'Cloneable', 'Appendable', 'Readable', 'Thread', 'ThreadGroup', 'ClassLoader',
|
|
95
|
+
'ThreadLocal', 'InheritableThreadLocal', 'ClassValue', 'SecurityManager', 'Process', 'Record'];
|
|
96
|
+
|
|
18
97
|
const STRUCTURAL_TRAITS = {
|
|
19
98
|
typeSystem: 'structural',
|
|
99
|
+
// A type's identity is its declaring module (a source file or inline
|
|
100
|
+
// module), not the directory (fix #384): two same-name types in two
|
|
101
|
+
// modules of one directory are distinct (JS/TS, Python modules, Rust
|
|
102
|
+
// modules). Go packages, Java packages, C# namespaces and C/C++
|
|
103
|
+
// namespaces keep their own identity rules.
|
|
104
|
+
moduleTypeIdentity: true,
|
|
105
|
+
// Leading path segments that always name the language's own standard
|
|
106
|
+
// library (fix #371): a type written under one of them (`std::fs::File`)
|
|
107
|
+
// denotes an external type, never a same-name project type.
|
|
108
|
+
standardPathRoots: [],
|
|
109
|
+
// How an unqualified type name reaches a file beyond its own
|
|
110
|
+
// declarations and single-name imports (fix #372): 'package-on-demand'
|
|
111
|
+
// (Java: same package, then on-demand imports plus the implicit ones),
|
|
112
|
+
// 'namespace-usings' (C#: enclosing namespaces, then using directives,
|
|
113
|
+
// global usings and project-file <Using> items), or null.
|
|
114
|
+
typeNameScoping: null,
|
|
115
|
+
// Types every file imports on demand without writing it (Java java.lang,
|
|
116
|
+
// JLS 7.3): { module, names }. A project type of the same name in another
|
|
117
|
+
// package is visible only through an import.
|
|
118
|
+
implicitTypeImport: null,
|
|
20
119
|
methodCallInclusion: 'explicit',
|
|
21
120
|
packageScope: 'file',
|
|
22
121
|
hasReceiverPackageCalls: false,
|
|
@@ -42,6 +141,14 @@ const STRUCTURAL_TRAITS = {
|
|
|
42
141
|
// captured as modifiers) or capitalization rules (Go) set false: there
|
|
43
142
|
// the member's own marker decides.
|
|
44
143
|
implicitlyPublicMembers: true,
|
|
144
|
+
// Member names the language itself keeps private to the declaring class
|
|
145
|
+
// (JS/TS `#x`; Python mangles `__x`): such a member never implements or
|
|
146
|
+
// overrides a supertype's member. A leading `_` is a naming convention
|
|
147
|
+
// only: Node stream hooks (`_transform`, `_flush`) and Python
|
|
148
|
+
// `TextWrapper._handle_long_word` are members an outside base calls
|
|
149
|
+
// (fix #397). Languages without the trait keep the conservative
|
|
150
|
+
// underscore rule.
|
|
151
|
+
privateMemberName: name => String(name).startsWith('#'),
|
|
45
152
|
// A bare (receiver-less) name can never denote a METHOD here — JS/Python
|
|
46
153
|
// methods are reached through their receiver; only a rebound alias could,
|
|
47
154
|
// which is separate name-level evidence. Java sets true: `execute()`
|
|
@@ -52,9 +159,63 @@ const STRUCTURAL_TRAITS = {
|
|
|
52
159
|
// assignment rebinds functions onto objects (obj.print = print), so the
|
|
53
160
|
// #218b gate routes such calls visible instead of excluding (fix #220).
|
|
54
161
|
methodCallReachesFunctions: true,
|
|
162
|
+
// Generic-parameter receivers can reach inherited/structural satisfiers
|
|
163
|
+
// (see the nominal preset); only Rust closes dispatch over bound traits.
|
|
164
|
+
genericBoundDispatchClosed: false,
|
|
55
165
|
// Whether `Type(...)` constructs a class without a `new` token. Python
|
|
56
166
|
// classes are ordinary callable objects; JS/TS classes require `new`.
|
|
57
167
|
classesCallableWithoutNew: false,
|
|
168
|
+
// Whether a decorator REPLACES the callable it decorates with its own
|
|
169
|
+
// return value (Python/JS decorators, fix #364). What a decorated async
|
|
170
|
+
// function's call returns is then decided by the decorator, not by the
|
|
171
|
+
// `async` keyword. Attribute/annotation languages (C#, Java, Rust) only
|
|
172
|
+
// attach metadata and set false.
|
|
173
|
+
decoratorsWrapCallables: true,
|
|
174
|
+
callableDecorators: null,
|
|
175
|
+
// Runtime-provided global functions that return an awaitable (JS
|
|
176
|
+
// `fetch`), audited by audit-async as bare calls (fix #364).
|
|
177
|
+
knownAsyncGlobals: null,
|
|
178
|
+
// Whether a plain assignment `x = v` inside a function declares a local
|
|
179
|
+
// (Python, unless `global`/`nonlocal` names it). Elsewhere an undeclared
|
|
180
|
+
// name is an outer variable or an implicit-this field, so an awaitable
|
|
181
|
+
// stored there flows on (audit-async, fix #380).
|
|
182
|
+
assignmentDeclaresLocal: false,
|
|
183
|
+
// Whether a type's generic arity is part of its identity (C#: `Outcome`,
|
|
184
|
+
// `Outcome<T>` and `Outcome<T1, T2>` are distinct types that coexist).
|
|
185
|
+
// Java erases generics and TS/Rust/C++ cannot declare two same-name types
|
|
186
|
+
// differing only in arity, so a written arity decides nothing there.
|
|
187
|
+
// Drives arity-aware constructor, type-qualified and typed-receiver
|
|
188
|
+
// resolution (fix #380).
|
|
189
|
+
genericArityIsIdentity: false,
|
|
190
|
+
// Whether a generic method's type arguments are inferred from its call
|
|
191
|
+
// ARGUMENTS only (C#: a type parameter no parameter type mentions makes
|
|
192
|
+
// the method inapplicable without explicit type arguments). Java infers
|
|
193
|
+
// from the target type too; C++ templates are not overload-applicability
|
|
194
|
+
// evidence here (fix #393).
|
|
195
|
+
typeArgumentsFromArgumentsOnly: false,
|
|
196
|
+
// Test functions the toolchain binds to a declared identifier by NAME
|
|
197
|
+
// (fix #383): Go examples (GO_EXAMPLE_NAMES). Rust doc tests and
|
|
198
|
+
// Java/C#/Python doc references are comment text, listed by plan as
|
|
199
|
+
// text dependencies; no other language checks a test function's name
|
|
200
|
+
// against a declaration.
|
|
201
|
+
nameBoundTestFunctions: null,
|
|
202
|
+
// Whether a top-level type is declared in a source file named after it
|
|
203
|
+
// (fix #386): javac requires `public class Widget` in Widget.java, so a
|
|
204
|
+
// type rename renames that file too.
|
|
205
|
+
typeNamedSourceFile: false,
|
|
206
|
+
// Documentation references the compiler resolves (fix #390): C# XML doc
|
|
207
|
+
// `cref` attributes (CS1574 when documentation is generated). A member
|
|
208
|
+
// rename edits the crefs that name the renamed member.
|
|
209
|
+
docCrefReferences: false,
|
|
210
|
+
// Whether a method's simple name in value position is a method group
|
|
211
|
+
// (fix #395, C#: `new(Build, 4)`, `x += OnChanged`, `.Select(Build)`,
|
|
212
|
+
// `Func<int> f = Build`): plan renames it by the language's simple-name
|
|
213
|
+
// lookup. Java spells method values apart (`Type::m`).
|
|
214
|
+
methodGroupReferences: false,
|
|
215
|
+
// Whether a class may declare a field and a method of one name, so a
|
|
216
|
+
// paren-less member access (`this.size`) names the field and a method
|
|
217
|
+
// value is spelled apart (`obj::m`, `Type::m`) (fix #390: Java, Rust).
|
|
218
|
+
fieldsAndMethodsSeparate: false,
|
|
58
219
|
// Whether call sites bind arguments by parameter NAME (Python keyword
|
|
59
220
|
// arguments). Drives verify/check keyword binding validation (fix #281):
|
|
60
221
|
// keyword names checked against the signature, keyword-only/positional-only
|
|
@@ -62,9 +223,49 @@ const STRUCTURAL_TRAITS = {
|
|
|
62
223
|
// arguments must stay false — a JS call with fewer args than params is
|
|
63
224
|
// legal, so required-coverage checks would false-flag.
|
|
64
225
|
keywordArguments: false,
|
|
226
|
+
// Members whose NAME is fixed by the language itself (fix #360): Python
|
|
227
|
+
// data-model dunders (`__eq__`, `__iter__`) implement a protocol the
|
|
228
|
+
// interpreter dispatches by name. Renaming one never renames a project
|
|
229
|
+
// symbol; it withdraws the protocol. null = no such name class.
|
|
230
|
+
languageProtocolMember: null,
|
|
231
|
+
// Types the compiler itself references when a language feature is used
|
|
232
|
+
// (fix #380; C# polyfills such as IsExternalInit). null = none.
|
|
233
|
+
languageProtocolType: null,
|
|
234
|
+
// Runtime reflection vocabulary (fix #307, #363): calls that select a
|
|
235
|
+
// member by a string name. `functions` match the callee text (name and
|
|
236
|
+
// receiver by argument position), `members` match a method name on a
|
|
237
|
+
// receiver object; `computedMembers` treats `obj[expr]` as member access;
|
|
238
|
+
// `membersOnly` when the API can only reach class members; `formatCalls`
|
|
239
|
+
// are string-formatting calls whose first argument is a format template;
|
|
240
|
+
// `selfTypeCalls`/`typeLiteralNodes` spell "the runtime class of this" and
|
|
241
|
+
// class literals. `hintPattern` locates vocabulary spellings whose
|
|
242
|
+
// enclosing call nodes are inspected (the AST decides); `namespaceLookups` are module-namespace lookups by
|
|
243
|
+
// name (eval, globals()[k]). `family` groups languages sharing one
|
|
244
|
+
// runtime. null = no runtime reflection.
|
|
245
|
+
reflectionApi: null,
|
|
246
|
+
// Members every class inherits from the implicit root supertype, keyed by
|
|
247
|
+
// name with their parameter count (Java Object.toString/equals/...,
|
|
248
|
+
// C# object.ToString/Equals/...). A same-name same-arity member overrides
|
|
249
|
+
// the root contract even without an override marker (fix #360). null =
|
|
250
|
+
// no universal supertype surface.
|
|
251
|
+
universalSupertypeMembers: null,
|
|
252
|
+
// Whether macro definitions hold token-level TEMPLATES whose identifiers
|
|
253
|
+
// bind at each expansion site (Rust macro_rules!). Plan renames model
|
|
254
|
+
// their transcriber tokens explicitly (fix #360).
|
|
255
|
+
macroTemplateBodies: false,
|
|
256
|
+
// Whether a translation unit sees every declaration reachable through its
|
|
257
|
+
// transitive #include closure (C/C++ textual inclusion, fix #361). Free
|
|
258
|
+
// function visibility then follows the include closure, not one import
|
|
259
|
+
// hop, and several external-linkage definitions of one name are resolved
|
|
260
|
+
// by the linker, which source evidence alone cannot choose between.
|
|
261
|
+
textualIncludes: false,
|
|
65
262
|
};
|
|
66
263
|
const NOMINAL_TRAITS = {
|
|
67
264
|
typeSystem: 'nominal',
|
|
265
|
+
moduleTypeIdentity: false,
|
|
266
|
+
standardPathRoots: [],
|
|
267
|
+
typeNameScoping: null,
|
|
268
|
+
implicitTypeImport: null,
|
|
68
269
|
methodCallInclusion: 'auto',
|
|
69
270
|
packageScope: 'file',
|
|
70
271
|
hasReceiverPackageCalls: false,
|
|
@@ -76,6 +277,13 @@ const NOMINAL_TRAITS = {
|
|
|
76
277
|
// named arguments but its parser doesn't record them yet; the trait stays
|
|
77
278
|
// false there until that family is measured (fix #281, classified-deferred).
|
|
78
279
|
keywordArguments: false,
|
|
280
|
+
// See STRUCTURAL_TRAITS for the three contract-membership traits (fix #360).
|
|
281
|
+
languageProtocolMember: null,
|
|
282
|
+
languageProtocolType: null,
|
|
283
|
+
reflectionApi: null,
|
|
284
|
+
universalSupertypeMembers: null,
|
|
285
|
+
macroTemplateBodies: false,
|
|
286
|
+
textualIncludes: false,
|
|
79
287
|
// Go/Java/Rust have no default parameter values — see STRUCTURAL_TRAITS.
|
|
80
288
|
hasDefaultParams: false,
|
|
81
289
|
// Whether ANY instance method call can dynamically dispatch to a subtype
|
|
@@ -122,6 +330,13 @@ const NOMINAL_TRAITS = {
|
|
|
122
330
|
// parens and Java requires .apply() — a dot-call provably never binds a
|
|
123
331
|
// free function (fix #220, ripgrep-measured).
|
|
124
332
|
methodCallReachesFunctions: false,
|
|
333
|
+
// Whether a call on a receiver typed by a GENERIC PARAMETER can reach a
|
|
334
|
+
// concrete method only through the traits in that parameter's bound
|
|
335
|
+
// closure (fix #368). Rust: no implementation inheritance, so
|
|
336
|
+
// `c.split_at()` with `C: Consumer` never reaches a Producer impl member
|
|
337
|
+
// or an inherent method. Java/C#/Go/TS: a subclass or structural
|
|
338
|
+
// satisfier can inherit the target through the bound, so false.
|
|
339
|
+
genericBoundDispatchClosed: false,
|
|
125
340
|
// Whether a paren-less member access (x.name) can denote a METHOD. Rust
|
|
126
341
|
// sets true for the inverse — `x.name` is ALWAYS a field there (method
|
|
127
342
|
// values are path-only: Type::method), so a member-access reference
|
|
@@ -131,6 +346,81 @@ const NOMINAL_TRAITS = {
|
|
|
131
346
|
// methods — false. Per-language override, not preset-wide.
|
|
132
347
|
memberAccessNeverMethod: false,
|
|
133
348
|
classesCallableWithoutNew: false,
|
|
349
|
+
decoratorsWrapCallables: false,
|
|
350
|
+
callableDecorators: null,
|
|
351
|
+
knownAsyncGlobals: null,
|
|
352
|
+
assignmentDeclaresLocal: false,
|
|
353
|
+
genericArityIsIdentity: false,
|
|
354
|
+
typeArgumentsFromArgumentsOnly: false,
|
|
355
|
+
// Test functions the toolchain binds to a declared identifier by NAME
|
|
356
|
+
// (fix #383); see GO_EXAMPLE_NAMES. null: no such convention.
|
|
357
|
+
nameBoundTestFunctions: null,
|
|
358
|
+
// A top-level type lives in a file named after it (fix #386, Java).
|
|
359
|
+
typeNamedSourceFile: false,
|
|
360
|
+
// Compiler-resolved doc references (fix #390, C# crefs).
|
|
361
|
+
docCrefReferences: false,
|
|
362
|
+
// Simple-name method groups (fix #395, C#).
|
|
363
|
+
methodGroupReferences: false,
|
|
364
|
+
// A static import of a type brings only the members the type itself
|
|
365
|
+
// declares, never inherited ones (fix #392, C# `using static`). Java's
|
|
366
|
+
// static imports reach inherited static members.
|
|
367
|
+
staticImportsDeclaredOnly: false,
|
|
368
|
+
// Fields and methods are separate namespaces (fix #390, Java/Rust).
|
|
369
|
+
fieldsAndMethodsSeparate: false,
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Go example functions (fix #383): go vet's `tests` analyzer (run by
|
|
374
|
+
* `go test`) and godoc bind `Example<Ident>[_<Member>][_<suffix>]` in a
|
|
375
|
+
* _test.go file to the identifier it names. The name is split the way vet
|
|
376
|
+
* splits it: at most three parts at '_', and a part starting with a
|
|
377
|
+
* lower-case letter is a suffix, not a member. Renaming the identifier or
|
|
378
|
+
* member without the example fails vet ("refers to unknown field or method").
|
|
379
|
+
*/
|
|
380
|
+
const GO_EXAMPLE_NAMES = {
|
|
381
|
+
kind: 'go-example',
|
|
382
|
+
prefix: 'Example',
|
|
383
|
+
testFile: /_test\.go$/,
|
|
384
|
+
parse(fnName) {
|
|
385
|
+
if (typeof fnName !== 'string' || !fnName.startsWith('Example')) return null;
|
|
386
|
+
const exName = fnName.slice('Example'.length);
|
|
387
|
+
const first = exName.indexOf('_');
|
|
388
|
+
const ident = first < 0 ? exName : exName.slice(0, first);
|
|
389
|
+
if (!ident) return null; // Example / Example_suffix: package examples
|
|
390
|
+
let member = null;
|
|
391
|
+
if (first >= 0) {
|
|
392
|
+
const after = exName.slice(first + 1);
|
|
393
|
+
const second = after.indexOf('_');
|
|
394
|
+
const part = second < 0 ? after : after.slice(0, second);
|
|
395
|
+
if (part && !/^\p{Ll}/u.test(part)) member = part;
|
|
396
|
+
}
|
|
397
|
+
return {
|
|
398
|
+
ident,
|
|
399
|
+
member,
|
|
400
|
+
// The function name with its ident and/or member part replaced.
|
|
401
|
+
rename({ ident: newIdent = ident, member: newMember = member } = {}) {
|
|
402
|
+
const tail = exName.slice(ident.length);
|
|
403
|
+
if (!member) return `Example${newIdent}${tail}`;
|
|
404
|
+
return `Example${newIdent}_${newMember}${tail.slice(1 + member.length)}`;
|
|
405
|
+
},
|
|
406
|
+
};
|
|
407
|
+
},
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
// JS/TS runtime protocol members: JSON.stringify calls toJSON, await and
|
|
411
|
+
// promise resolution call then, coercion calls toString/valueOf, and
|
|
412
|
+
// well-known symbols (`[Symbol.iterator]`) are invoked by the runtime.
|
|
413
|
+
const JS_PROTOCOL_MEMBERS = protocolMembers([
|
|
414
|
+
{ names: ['toJSON', 'then', 'toString', 'valueOf', 'toLocaleString'], member: true },
|
|
415
|
+
{ pattern: /^\[Symbol\.[A-Za-z]+\]$/, member: true },
|
|
416
|
+
]);
|
|
417
|
+
const JS_REFLECTION_API = {
|
|
418
|
+
hintPattern: /\bReflect\s*\.\s*(?:get|set|has|deleteProperty)\b|\beval\s*\(/,
|
|
419
|
+
functions: [{ callee: /^(?:globalThis\.)?Reflect\.(?:get|set|has|deleteProperty)$/,
|
|
420
|
+
nameArg: 1, receiverArg: 0, qualified: true }],
|
|
421
|
+
computedMembers: true,
|
|
422
|
+
namespaceLookups: { calls: /^eval$/ },
|
|
423
|
+
family: 'javascript',
|
|
134
424
|
};
|
|
135
425
|
|
|
136
426
|
// Language configurations
|
|
@@ -144,7 +434,10 @@ const LANGUAGES = {
|
|
|
144
434
|
traits: {
|
|
145
435
|
...STRUCTURAL_TRAITS,
|
|
146
436
|
selfParam: ['this'],
|
|
437
|
+
reflectionApi: JS_REFLECTION_API,
|
|
438
|
+
languageProtocolMember: JS_PROTOCOL_MEMBERS,
|
|
147
439
|
storedPromises: true,
|
|
440
|
+
knownAsyncGlobals: ['fetch'],
|
|
148
441
|
testFileCandidates: (base, ext) => [`${base}.test${ext}`, `${base}.spec${ext}`, `${base}.test.ts`, `${base}.test.js`, `${base}.spec.ts`, `${base}.spec.js`],
|
|
149
442
|
testDirs: ['__tests__'],
|
|
150
443
|
},
|
|
@@ -158,7 +451,10 @@ const LANGUAGES = {
|
|
|
158
451
|
traits: {
|
|
159
452
|
...STRUCTURAL_TRAITS,
|
|
160
453
|
selfParam: ['this'],
|
|
454
|
+
reflectionApi: JS_REFLECTION_API,
|
|
455
|
+
languageProtocolMember: JS_PROTOCOL_MEMBERS,
|
|
161
456
|
storedPromises: true,
|
|
457
|
+
knownAsyncGlobals: ['fetch'],
|
|
162
458
|
testFileCandidates: (base, ext) => [`${base}.test${ext}`, `${base}.spec${ext}`, `${base}.test.ts`, `${base}.test.js`, `${base}.spec.ts`, `${base}.spec.js`],
|
|
163
459
|
testDirs: ['__tests__'],
|
|
164
460
|
},
|
|
@@ -172,7 +468,10 @@ const LANGUAGES = {
|
|
|
172
468
|
traits: {
|
|
173
469
|
...STRUCTURAL_TRAITS,
|
|
174
470
|
selfParam: ['this'],
|
|
471
|
+
reflectionApi: JS_REFLECTION_API,
|
|
472
|
+
languageProtocolMember: JS_PROTOCOL_MEMBERS,
|
|
175
473
|
storedPromises: true,
|
|
474
|
+
knownAsyncGlobals: ['fetch'],
|
|
176
475
|
testFileCandidates: (base, ext) => [`${base}.test${ext}`, `${base}.spec${ext}`, `${base}.test.ts`, `${base}.test.js`, `${base}.spec.ts`, `${base}.spec.js`],
|
|
177
476
|
testDirs: ['__tests__'],
|
|
178
477
|
},
|
|
@@ -191,10 +490,70 @@ const LANGUAGES = {
|
|
|
191
490
|
// and query code treats such receivers as module receivers. JS
|
|
192
491
|
// from-imports bind values only (`import * as ns` is parser-marked).
|
|
193
492
|
submoduleImports: true,
|
|
493
|
+
// `__x` (not a dunder) is name-mangled per class (fix #397).
|
|
494
|
+
privateMemberName: name => /^__/.test(String(name)) && !/__$/.test(String(name)),
|
|
194
495
|
classesCallableWithoutNew: true,
|
|
195
496
|
// Call sites bind by parameter name (f(x=1)); signatures carry
|
|
196
497
|
// keyword-only (`*`) and positional-only (`/`) markers (fix #281).
|
|
197
498
|
keywordArguments: true,
|
|
499
|
+
assignmentDeclaresLocal: true,
|
|
500
|
+
// Calling an async def creates a coroutine object in any scope:
|
|
501
|
+
// audit-async checks such calls in sync functions too, and
|
|
502
|
+
// follows a local assigned from the call to the uses that need
|
|
503
|
+
// its result (fix #398).
|
|
504
|
+
storedCoroutines: true,
|
|
505
|
+
// A name bound in one scope only in exclusive branches (`if
|
|
506
|
+
// sys.platform ...: def f` / `else: def f`, `try: from m import
|
|
507
|
+
// f` / `except ImportError: def f`) is one item with an
|
|
508
|
+
// alternative per runtime configuration (fix #398F).
|
|
509
|
+
conditionalDefinitions: 'branch',
|
|
510
|
+
// A def inside a function body binds a local of that function: a
|
|
511
|
+
// bare name outside it never names it (fix #402), unless the
|
|
512
|
+
// function declares the name `global`.
|
|
513
|
+
nestedItemsBlockScoped: true,
|
|
514
|
+
// Decorators with a known effect on what calling the decorated
|
|
515
|
+
// function returns (fix #364), as resolved qualified names (a
|
|
516
|
+
// trailing `()` marks a decorator factory call). Any other
|
|
517
|
+
// decorator makes an async function's result unknown.
|
|
518
|
+
callableDecorators: {
|
|
519
|
+
builtins: ['staticmethod', 'classmethod', 'property', 'callable', 'isinstance'],
|
|
520
|
+
transparent: [
|
|
521
|
+
'builtins.staticmethod', 'builtins.classmethod', 'abc.abstractmethod',
|
|
522
|
+
'typing.override', 'typing_extensions.override', 'typing.final',
|
|
523
|
+
'typing_extensions.final', 'typing.overload', 'typing_extensions.overload',
|
|
524
|
+
'functools.wraps()',
|
|
525
|
+
],
|
|
526
|
+
asyncContextManagers: ['contextlib.asynccontextmanager'],
|
|
527
|
+
// Standard-library decorators that never read the decorated
|
|
528
|
+
// callable's name (fix #392): a rename of the definition and
|
|
529
|
+
// its references is complete under them.
|
|
530
|
+
namePreserving: [
|
|
531
|
+
'builtins.property', 'abc.abstractproperty', 'abc.abstractclassmethod',
|
|
532
|
+
'abc.abstractstaticmethod', 'functools.cached_property', 'functools.cache',
|
|
533
|
+
'functools.lru_cache', 'functools.lru_cache()', 'functools.singledispatch',
|
|
534
|
+
'functools.singledispatchmethod', 'functools.total_ordering',
|
|
535
|
+
'contextlib.contextmanager', 'typing.no_type_check',
|
|
536
|
+
'typing.runtime_checkable', 'typing_extensions.runtime_checkable',
|
|
537
|
+
'typing_extensions.deprecated()', 'warnings.deprecated()',
|
|
538
|
+
'dataclasses.dataclass', 'dataclasses.dataclass()', 'enum.unique',
|
|
539
|
+
'unittest.mock.patch()', 'unittest.mock.patch.object()', 'unittest.mock.patch.dict()',
|
|
540
|
+
'unittest.mock.patch.multiple()', 'unittest.skip()', 'unittest.skipIf()',
|
|
541
|
+
'unittest.skipUnless()', 'unittest.expectedFailure',
|
|
542
|
+
],
|
|
543
|
+
},
|
|
544
|
+
languageProtocolMember: protocolMembers([
|
|
545
|
+
// Data-model dunders (__eq__, __init_subclass__, __set_name__,
|
|
546
|
+
// __reduce__, module __getattr__) are dispatched by name.
|
|
547
|
+
{ pattern: /^__[A-Za-z][A-Za-z0-9_]*__$/ },
|
|
548
|
+
// Enum machinery hooks (auto() values, lookup fallback).
|
|
549
|
+
{ names: ['_generate_next_value_', '_missing_'], member: true,
|
|
550
|
+
owners: ['Enum', 'IntEnum', 'StrEnum', 'Flag', 'IntFlag', 'ReprEnum'] },
|
|
551
|
+
]),
|
|
552
|
+
reflectionApi: {
|
|
553
|
+
hintPattern: /\b(?:getattr|setattr|hasattr|delattr|eval)\s*\(|\b(?:globals|vars)\s*\(\s*\)\s*\[/,
|
|
554
|
+
functions: [{ callee: /^(?:getattr|setattr|hasattr|delattr)$/, nameArg: 1, receiverArg: 0 }],
|
|
555
|
+
namespaceLookups: { calls: /^eval$/, subscriptOf: /^(?:globals|vars)\(\)$/ },
|
|
556
|
+
},
|
|
198
557
|
testFileCandidates: (base, ext) => [`test_${base}.py`, `${base}_test.py`],
|
|
199
558
|
testDirs: ['tests'],
|
|
200
559
|
},
|
|
@@ -214,7 +573,14 @@ const LANGUAGES = {
|
|
|
214
573
|
hasDynamicImports: false,
|
|
215
574
|
typeQualifiedCallStyle: 'method-expr',
|
|
216
575
|
methodCallReachesFunctions: true,
|
|
576
|
+
reflectionApi: {
|
|
577
|
+
hintPattern: /\b(?:MethodByName|FieldByName)\b/,
|
|
578
|
+
members: [{ member: /^(?:MethodByName|FieldByName)$/, nameArg: 0 }],
|
|
579
|
+
membersOnly: true,
|
|
580
|
+
formatCalls: { 'fmt.Sprintf': 'percent' },
|
|
581
|
+
},
|
|
217
582
|
testFileCandidates: (base, ext) => [`${base}_test.go`],
|
|
583
|
+
nameBoundTestFunctions: GO_EXAMPLE_NAMES,
|
|
218
584
|
},
|
|
219
585
|
},
|
|
220
586
|
rust: {
|
|
@@ -227,8 +593,39 @@ const LANGUAGES = {
|
|
|
227
593
|
...NOMINAL_TRAITS,
|
|
228
594
|
selfParam: ['self', '&self', '&mut self', 'mut self'],
|
|
229
595
|
hasDynamicImports: false,
|
|
596
|
+
moduleTypeIdentity: true,
|
|
597
|
+
// Same-scope definitions of one name are configuration
|
|
598
|
+
// alternatives selected by `#[cfg]` (fix #376).
|
|
599
|
+
conditionalDefinitions: 'attribute',
|
|
230
600
|
typeQualifiedCallStyle: 'path',
|
|
601
|
+
standardPathRoots: ['std', 'core', 'alloc'],
|
|
602
|
+
// Call records say whether their value is consumed where it is
|
|
603
|
+
// produced (`valueConsumed`, `consumingMethod`; fix #371).
|
|
604
|
+
callValueFacts: true,
|
|
231
605
|
memberAccessNeverMethod: true,
|
|
606
|
+
fieldsAndMethodsSeparate: true,
|
|
607
|
+
genericBoundDispatchClosed: true,
|
|
608
|
+
macroTemplateBodies: true,
|
|
609
|
+
// Macro invocations are written `name!(...)` / `#[name]`: a
|
|
610
|
+
// separate namespace from fns and values (fix #377).
|
|
611
|
+
macroInvocationSyntax: 'marked',
|
|
612
|
+
// A fn declared inside a fn body is an item of that block: a
|
|
613
|
+
// bare path outside the block never names it (fix #377).
|
|
614
|
+
nestedItemsBlockScoped: true,
|
|
615
|
+
// Names every module sees without a `use` (the std prelude of
|
|
616
|
+
// editions 2015-2024 and the primitive types): a name no `use`,
|
|
617
|
+
// glob or item binds must be one of them, or come from a macro
|
|
618
|
+
// expansion (fix #386).
|
|
619
|
+
implicitPreludeNames: new Set([
|
|
620
|
+
'Option', 'Some', 'None', 'Result', 'Ok', 'Err', 'Vec', 'String', 'Box',
|
|
621
|
+
'ToString', 'ToOwned', 'Clone', 'Copy', 'Send', 'Sync', 'Sized', 'Unpin',
|
|
622
|
+
'Drop', 'Fn', 'FnMut', 'FnOnce', 'AsyncFn', 'AsyncFnMut', 'AsyncFnOnce',
|
|
623
|
+
'Iterator', 'IntoIterator', 'Extend', 'DoubleEndedIterator', 'ExactSizeIterator',
|
|
624
|
+
'FromIterator', 'Default', 'Eq', 'PartialEq', 'Ord', 'PartialOrd', 'AsRef',
|
|
625
|
+
'AsMut', 'Into', 'From', 'TryFrom', 'TryInto', 'Future', 'IntoFuture', 'Debug',
|
|
626
|
+
'Hash', 'bool', 'char', 'str', 'u8', 'u16', 'u32', 'u64', 'u128', 'usize',
|
|
627
|
+
'i8', 'i16', 'i32', 'i64', 'i128', 'isize', 'f32', 'f64',
|
|
628
|
+
]),
|
|
232
629
|
testFileCandidates: (base, ext) => [`${base}_test.rs`],
|
|
233
630
|
testDirs: ['tests'],
|
|
234
631
|
},
|
|
@@ -241,10 +638,72 @@ const LANGUAGES = {
|
|
|
241
638
|
treeSitterModule: () => require('tree-sitter-java'),
|
|
242
639
|
traits: {
|
|
243
640
|
...NOMINAL_TRAITS,
|
|
641
|
+
fieldsAndMethodsSeparate: true,
|
|
642
|
+
standardPathRoots: ['java', 'javax'],
|
|
643
|
+
typeNameScoping: 'package-on-demand',
|
|
644
|
+
typeNamedSourceFile: true,
|
|
645
|
+
// The public top-level types of java.lang (JDK 8-21, preview
|
|
646
|
+
// included): a spec-level list, not project knowledge.
|
|
647
|
+
implicitTypeImport: { module: 'java.lang', names: new Set([
|
|
648
|
+
'AbstractMethodError', 'Appendable', 'ArithmeticException',
|
|
649
|
+
'ArrayIndexOutOfBoundsException', 'ArrayStoreException', 'AssertionError',
|
|
650
|
+
'AutoCloseable', 'Boolean', 'BootstrapMethodError', 'Byte', 'Character',
|
|
651
|
+
'CharSequence', 'Class', 'ClassCastException', 'ClassCircularityError',
|
|
652
|
+
'ClassFormatError', 'ClassLoader', 'ClassNotFoundException', 'ClassValue',
|
|
653
|
+
'CloneNotSupportedException', 'Cloneable', 'Comparable', 'Compiler', 'Deprecated',
|
|
654
|
+
'Double', 'Enum', 'EnumConstantNotPresentException', 'Error', 'Exception',
|
|
655
|
+
'ExceptionInInitializerError', 'Float', 'FunctionalInterface',
|
|
656
|
+
'IllegalAccessError', 'IllegalAccessException', 'IllegalArgumentException',
|
|
657
|
+
'IllegalCallerException', 'IllegalMonitorStateException', 'IllegalStateException',
|
|
658
|
+
'IllegalThreadStateException', 'IncompatibleClassChangeError',
|
|
659
|
+
'IndexOutOfBoundsException', 'InheritableThreadLocal', 'InstantiationError',
|
|
660
|
+
'InstantiationException', 'Integer', 'InternalError', 'InterruptedException',
|
|
661
|
+
'Iterable', 'LayerInstantiationException', 'LinkageError', 'Long',
|
|
662
|
+
'MatchException', 'Math', 'Module', 'ModuleLayer', 'NegativeArraySizeException',
|
|
663
|
+
'NoClassDefFoundError', 'NoSuchFieldError', 'NoSuchFieldException',
|
|
664
|
+
'NoSuchMethodError', 'NoSuchMethodException', 'NullPointerException', 'Number',
|
|
665
|
+
'NumberFormatException', 'Object', 'OutOfMemoryError', 'Override', 'Package',
|
|
666
|
+
'Process', 'ProcessBuilder', 'ProcessHandle', 'Readable', 'Record',
|
|
667
|
+
'ReflectiveOperationException', 'Runnable', 'Runtime', 'RuntimeException',
|
|
668
|
+
'RuntimePermission', 'SafeVarargs', 'ScopedValue', 'SecurityException',
|
|
669
|
+
'SecurityManager', 'Short', 'StackOverflowError', 'StackTraceElement',
|
|
670
|
+
'StackWalker', 'StrictMath', 'String', 'StringBuffer', 'StringBuilder',
|
|
671
|
+
'StringIndexOutOfBoundsException', 'StringTemplate', 'SuppressWarnings', 'System',
|
|
672
|
+
'Thread', 'ThreadDeath', 'ThreadGroup', 'ThreadLocal', 'Throwable',
|
|
673
|
+
'TypeNotPresentException', 'UnknownError', 'UnsatisfiedLinkError',
|
|
674
|
+
'UnsupportedClassVersionError', 'UnsupportedOperationException', 'VerifyError',
|
|
675
|
+
'VirtualMachineError', 'Void', 'WrongThreadException',
|
|
676
|
+
]) },
|
|
244
677
|
selfParam: ['this'],
|
|
245
678
|
allMethodsVirtual: true,
|
|
246
679
|
hasArityOverloads: true,
|
|
247
680
|
universalSupertype: 'Object',
|
|
681
|
+
universalSupertypeMembers: { toString: 0, equals: 1, hashCode: 0, clone: 0, finalize: 0 },
|
|
682
|
+
// Serialization callbacks and fields the JVM uses reflectively.
|
|
683
|
+
languageProtocolMember: protocolMembers([
|
|
684
|
+
// java.lang roots that implement java.io.Serializable (JDK API):
|
|
685
|
+
// a project subclass inherits the contract (fix #380). Any
|
|
686
|
+
// other out-of-project supertype but Object may implement it
|
|
687
|
+
// too (`extends IOException`, `extends ArrayList`): possible.
|
|
688
|
+
{ names: ['readObject', 'writeObject', 'readResolve', 'writeReplace', 'readObjectNoData',
|
|
689
|
+
'serialVersionUID', 'serialPersistentFields'],
|
|
690
|
+
member: true, owners: ['Serializable', 'Externalizable', 'Number', 'Throwable',
|
|
691
|
+
'Exception', 'RuntimeException', 'Error'],
|
|
692
|
+
// Enum constants are serialized by name: readObject,
|
|
693
|
+
// writeObject, readResolve, writeReplace, readObjectNoData and
|
|
694
|
+
// serialVersionUID/serialPersistentFields of an enum type are
|
|
695
|
+
// ignored (Java Object Serialization Specification 1.12).
|
|
696
|
+
exceptOwnerKinds: ['enum'],
|
|
697
|
+
possibleViaExternal: { except: JAVA_LANG_NOT_SERIALIZABLE, shape: javaSerializationShape } },
|
|
698
|
+
]),
|
|
699
|
+
reflectionApi: {
|
|
700
|
+
hintPattern: /\b(?:getMethod|getDeclaredMethod|getField|getDeclaredField)\b/,
|
|
701
|
+
members: [{ member: /^(?:getMethod|getDeclaredMethod|getField|getDeclaredField)$/, nameArg: 0 }],
|
|
702
|
+
membersOnly: true,
|
|
703
|
+
formatCalls: { 'String.format': 'percent' },
|
|
704
|
+
selfTypeCalls: ['getClass'],
|
|
705
|
+
typeLiteralNodes: ['class_literal'],
|
|
706
|
+
},
|
|
248
707
|
bareCallReachesMethods: true,
|
|
249
708
|
testFileCandidates: (base, ext) => [`${base}Test.java`, `${base}Tests.java`, `${base}TestCase.java`],
|
|
250
709
|
},
|
|
@@ -261,6 +720,13 @@ const LANGUAGES = {
|
|
|
261
720
|
packageScope: 'file',
|
|
262
721
|
hasDynamicImports: false,
|
|
263
722
|
exportVisibility: 'linkage',
|
|
723
|
+
// Same-signature definitions in one file are `#if` alternatives.
|
|
724
|
+
conditionalDefinitions: 'preprocessor',
|
|
725
|
+
textualIncludes: true,
|
|
726
|
+
// Function-like macros are invoked with call syntax: a call
|
|
727
|
+
// spelled NAME( where #define NAME( is in effect is the macro
|
|
728
|
+
// (fix #377).
|
|
729
|
+
macroInvocationSyntax: 'call',
|
|
264
730
|
typeQualifiedCallStyle: 'static',
|
|
265
731
|
bareCallReachesMethods: false,
|
|
266
732
|
methodCallReachesFunctions: true,
|
|
@@ -281,9 +747,20 @@ const LANGUAGES = {
|
|
|
281
747
|
selfParam: ['this'],
|
|
282
748
|
packageScope: 'file',
|
|
283
749
|
hasDynamicImports: false,
|
|
750
|
+
conditionalDefinitions: 'preprocessor',
|
|
284
751
|
exportVisibility: 'linkage',
|
|
752
|
+
textualIncludes: true,
|
|
753
|
+
// Function-like macros are invoked with call syntax: a call
|
|
754
|
+
// spelled NAME( where #define NAME( is in effect is the macro
|
|
755
|
+
// (fix #377).
|
|
756
|
+
macroInvocationSyntax: 'call',
|
|
285
757
|
typeQualifiedCallStyle: 'path',
|
|
758
|
+
standardPathRoots: ['std'],
|
|
286
759
|
hasArityOverloads: true,
|
|
760
|
+
// Range-for lowers onto member begin()/end() with no name token.
|
|
761
|
+
languageProtocolMember: protocolMembers([
|
|
762
|
+
{ names: ['begin', 'end'], member: true },
|
|
763
|
+
]),
|
|
287
764
|
// C++ instance dispatch is virtual only when the declaration
|
|
288
765
|
// explicitly says virtual/override (unlike Java's implicit rule).
|
|
289
766
|
explicitVirtualDispatch: true,
|
|
@@ -305,6 +782,11 @@ const LANGUAGES = {
|
|
|
305
782
|
treeSitterModule: () => require('tree-sitter-c-sharp'),
|
|
306
783
|
traits: {
|
|
307
784
|
...NOMINAL_TRAITS,
|
|
785
|
+
standardPathRoots: ['System'],
|
|
786
|
+
conditionalDefinitions: 'preprocessor',
|
|
787
|
+
typeNameScoping: 'namespace-usings',
|
|
788
|
+
genericArityIsIdentity: true,
|
|
789
|
+
typeArgumentsFromArgumentsOnly: true,
|
|
308
790
|
selfParam: ['this', 'base'],
|
|
309
791
|
packageScope: 'namespace',
|
|
310
792
|
hasDynamicImports: false,
|
|
@@ -312,6 +794,42 @@ const LANGUAGES = {
|
|
|
312
794
|
bareCallReachesMethods: true,
|
|
313
795
|
typeQualifiedCallStyle: 'static',
|
|
314
796
|
universalSupertype: 'Object',
|
|
797
|
+
universalSupertypeMembers: { ToString: 0, Equals: 1, GetHashCode: 0, Finalize: 0 },
|
|
798
|
+
docCrefReferences: true,
|
|
799
|
+
methodGroupReferences: true,
|
|
800
|
+
staticImportsDeclaredOnly: true,
|
|
801
|
+
// Pattern-bound members the compiler lowers syntax onto
|
|
802
|
+
// (foreach, await, deconstruction) - invoked with no name token.
|
|
803
|
+
languageProtocolMember: protocolMembers([
|
|
804
|
+
{ names: ['GetEnumerator', 'GetAsyncEnumerator', 'MoveNext', 'MoveNextAsync', 'Current',
|
|
805
|
+
'GetAwaiter', 'GetResult', 'IsCompleted', 'OnCompleted', 'UnsafeOnCompleted',
|
|
806
|
+
'Deconstruct'], member: true },
|
|
807
|
+
]),
|
|
808
|
+
// Compiler-required types a feature's lowering names (C# spec;
|
|
809
|
+
// polyfilled by projects targeting older frameworks).
|
|
810
|
+
languageProtocolType: protocolTypes([
|
|
811
|
+
{ names: ['IsExternalInit'], namespace: 'System.Runtime.CompilerServices', features: ['init'] },
|
|
812
|
+
{ names: ['RequiredMemberAttribute', 'CompilerFeatureRequiredAttribute'],
|
|
813
|
+
namespace: 'System.Runtime.CompilerServices', features: ['required'] },
|
|
814
|
+
{ names: ['ExtensionAttribute'], namespace: 'System.Runtime.CompilerServices',
|
|
815
|
+
features: ['extension-method'] },
|
|
816
|
+
{ names: ['NullableAttribute', 'NullableContextAttribute', 'NullablePublicOnlyAttribute'],
|
|
817
|
+
namespace: 'System.Runtime.CompilerServices', features: ['nullable'] },
|
|
818
|
+
{ names: ['IsByRefLikeAttribute'], namespace: 'System.Runtime.CompilerServices', features: ['ref-struct'] },
|
|
819
|
+
{ names: ['IsReadOnlyAttribute'], namespace: 'System.Runtime.CompilerServices', features: ['readonly'] },
|
|
820
|
+
{ names: ['IsUnmanagedAttribute'], namespace: 'System.Runtime.CompilerServices', features: ['unmanaged'] },
|
|
821
|
+
{ names: ['TupleElementNamesAttribute'], namespace: 'System.Runtime.CompilerServices',
|
|
822
|
+
features: ['tuple-names'] },
|
|
823
|
+
{ names: ['Index', 'Range'], namespace: 'System', features: ['index-range'] },
|
|
824
|
+
]),
|
|
825
|
+
reflectionApi: {
|
|
826
|
+
hintPattern: /\b(?:GetMethod|GetProperty|GetField|GetMember)\b/,
|
|
827
|
+
members: [{ member: /^(?:GetMethod|GetProperty|GetField|GetMember)$/, nameArg: 0 }],
|
|
828
|
+
membersOnly: true,
|
|
829
|
+
formatCalls: { 'string.Format': 'brace', 'String.Format': 'brace' },
|
|
830
|
+
selfTypeCalls: ['GetType'],
|
|
831
|
+
typeLiteralNodes: ['typeof_expression'],
|
|
832
|
+
},
|
|
315
833
|
testFileCandidates: (base, ext) => [
|
|
316
834
|
`${base}Tests${ext}`, `${base}Test${ext}`, `${base}.Tests${ext}`,
|
|
317
835
|
],
|
|
@@ -328,6 +846,7 @@ const LANGUAGES = {
|
|
|
328
846
|
...STRUCTURAL_TRAITS,
|
|
329
847
|
selfParam: ['this'],
|
|
330
848
|
storedPromises: true,
|
|
849
|
+
knownAsyncGlobals: ['fetch'],
|
|
331
850
|
testFileCandidates: (base, ext) => [`${base}.test${ext}`, `${base}.spec${ext}`],
|
|
332
851
|
},
|
|
333
852
|
}
|
|
@@ -350,17 +869,215 @@ function loadTreeSitter() {
|
|
|
350
869
|
try {
|
|
351
870
|
TreeSitter = require('tree-sitter');
|
|
352
871
|
} catch (e) {
|
|
353
|
-
throw new
|
|
872
|
+
throw new UcnError(
|
|
354
873
|
'tree-sitter is required but not installed.\n' +
|
|
355
874
|
'Install with: npm install',
|
|
356
875
|
{ cause: e }
|
|
357
876
|
);
|
|
358
877
|
}
|
|
359
878
|
_installNodeTypeCache(TreeSitter);
|
|
879
|
+
_installNodeParentCache(TreeSitter);
|
|
880
|
+
_installNodeFieldCaches(TreeSitter);
|
|
360
881
|
}
|
|
361
882
|
return TreeSitter;
|
|
362
883
|
}
|
|
363
884
|
|
|
885
|
+
// Immutable facts of a node wrapper, read natively once (fix #385). They
|
|
886
|
+
// live in one record per wrapper, created with every field at once (fix
|
|
887
|
+
// #388): a wrapper gains a single property whichever fact is read first,
|
|
888
|
+
// instead of one per fact in read order.
|
|
889
|
+
const NODE_FACTS = Symbol('ucn.nodeFacts');
|
|
890
|
+
const UNREAD = Symbol('ucn.unread');
|
|
891
|
+
|
|
892
|
+
function NodeFacts() {
|
|
893
|
+
this.parent = UNREAD;
|
|
894
|
+
this.startIndex = -1;
|
|
895
|
+
this.endIndex = -1;
|
|
896
|
+
this.fields = null;
|
|
897
|
+
this.namedChildren = null;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
// Internal read of a node's named children without a defensive copy: the
|
|
901
|
+
// caller never mutates the array (the flat node list builder in utils.js).
|
|
902
|
+
const NAMED_CHILDREN_LIST = Symbol.for('ucn.namedChildrenList');
|
|
903
|
+
const NO_CHILDREN = Object.freeze([]);
|
|
904
|
+
|
|
905
|
+
function nodeFactsOf(node) {
|
|
906
|
+
let facts = node[NODE_FACTS];
|
|
907
|
+
if (facts === undefined) {
|
|
908
|
+
facts = new NodeFacts();
|
|
909
|
+
node[NODE_FACTS] = facts;
|
|
910
|
+
}
|
|
911
|
+
return facts;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
/**
|
|
915
|
+
* Same discipline as the parent cache for the other immutable facts hot
|
|
916
|
+
* walks re-read: byte offsets and field children (fix #385).
|
|
917
|
+
*/
|
|
918
|
+
function _installNodeFieldCaches(TS) {
|
|
919
|
+
try {
|
|
920
|
+
const base = TS?.SyntaxNode?.prototype;
|
|
921
|
+
if (!base) return;
|
|
922
|
+
const startDesc = Object.getOwnPropertyDescriptor(base, 'startIndex');
|
|
923
|
+
if (startDesc?.get && !startDesc.set) {
|
|
924
|
+
const nativeStart = startDesc.get;
|
|
925
|
+
Object.defineProperty(base, 'startIndex', {
|
|
926
|
+
configurable: true,
|
|
927
|
+
get() {
|
|
928
|
+
const facts = nodeFactsOf(this);
|
|
929
|
+
let value = facts.startIndex;
|
|
930
|
+
if (value < 0) {
|
|
931
|
+
value = nativeStart.call(this);
|
|
932
|
+
facts.startIndex = value;
|
|
933
|
+
}
|
|
934
|
+
return value;
|
|
935
|
+
},
|
|
936
|
+
});
|
|
937
|
+
}
|
|
938
|
+
const endDesc = Object.getOwnPropertyDescriptor(base, 'endIndex');
|
|
939
|
+
if (endDesc?.get && !endDesc.set) {
|
|
940
|
+
const nativeEnd = endDesc.get;
|
|
941
|
+
Object.defineProperty(base, 'endIndex', {
|
|
942
|
+
configurable: true,
|
|
943
|
+
get() {
|
|
944
|
+
const facts = nodeFactsOf(this);
|
|
945
|
+
let value = facts.endIndex;
|
|
946
|
+
if (value < 0) {
|
|
947
|
+
value = nativeEnd.call(this);
|
|
948
|
+
facts.endIndex = value;
|
|
949
|
+
}
|
|
950
|
+
return value;
|
|
951
|
+
},
|
|
952
|
+
});
|
|
953
|
+
}
|
|
954
|
+
// Named children (fix #388): the flat node list reads every named
|
|
955
|
+
// node's children once; later reads of the same node (child loops,
|
|
956
|
+
// counts, walks) are served from that array. Callers always get a
|
|
957
|
+
// fresh array, as from the binding.
|
|
958
|
+
const namedDesc = Object.getOwnPropertyDescriptor(base, 'namedChildren');
|
|
959
|
+
const countDesc = Object.getOwnPropertyDescriptor(base, 'namedChildCount');
|
|
960
|
+
const firstDesc = Object.getOwnPropertyDescriptor(base, 'firstNamedChild');
|
|
961
|
+
const lastDesc = Object.getOwnPropertyDescriptor(base, 'lastNamedChild');
|
|
962
|
+
const nativeNamedChild = base.namedChild;
|
|
963
|
+
if (namedDesc?.get && !namedDesc.set && countDesc?.get && !countDesc.set &&
|
|
964
|
+
firstDesc?.get && !firstDesc.set && lastDesc?.get && !lastDesc.set &&
|
|
965
|
+
typeof nativeNamedChild === 'function') {
|
|
966
|
+
const nativeNamed = namedDesc.get;
|
|
967
|
+
const nativeCount = countDesc.get;
|
|
968
|
+
const nativeFirst = firstDesc.get;
|
|
969
|
+
const nativeLast = lastDesc.get;
|
|
970
|
+
// A leaf's empty list is not stored unless the node already has
|
|
971
|
+
// a facts record: most named nodes are leaves, and a record per
|
|
972
|
+
// leaf would only hold an empty array (build-worker RSS).
|
|
973
|
+
const namedList = node => {
|
|
974
|
+
const existing = node[NODE_FACTS];
|
|
975
|
+
if (existing !== undefined && existing.namedChildren !== null) return existing.namedChildren;
|
|
976
|
+
const list = nativeNamed.call(node);
|
|
977
|
+
if (list.length > 0) nodeFactsOf(node).namedChildren = list;
|
|
978
|
+
else if (existing !== undefined) existing.namedChildren = NO_CHILDREN;
|
|
979
|
+
return list;
|
|
980
|
+
};
|
|
981
|
+
Object.defineProperty(base, NAMED_CHILDREN_LIST, {
|
|
982
|
+
configurable: true,
|
|
983
|
+
writable: true,
|
|
984
|
+
value() { return namedList(this); },
|
|
985
|
+
});
|
|
986
|
+
Object.defineProperty(base, 'namedChildren', {
|
|
987
|
+
configurable: true,
|
|
988
|
+
get() { return namedList(this).slice(); },
|
|
989
|
+
});
|
|
990
|
+
const cachedList = node => {
|
|
991
|
+
const facts = node[NODE_FACTS];
|
|
992
|
+
return facts === undefined ? null : facts.namedChildren;
|
|
993
|
+
};
|
|
994
|
+
Object.defineProperty(base, 'namedChildCount', {
|
|
995
|
+
configurable: true,
|
|
996
|
+
get() {
|
|
997
|
+
const list = cachedList(this);
|
|
998
|
+
return list === null ? nativeCount.call(this) : list.length;
|
|
999
|
+
},
|
|
1000
|
+
});
|
|
1001
|
+
Object.defineProperty(base, 'namedChild', {
|
|
1002
|
+
configurable: true,
|
|
1003
|
+
writable: true,
|
|
1004
|
+
value(index) {
|
|
1005
|
+
const list = cachedList(this);
|
|
1006
|
+
if (list !== null && Number.isInteger(index) && index >= 0 && index < list.length) {
|
|
1007
|
+
return list[index];
|
|
1008
|
+
}
|
|
1009
|
+
return nativeNamedChild.call(this, index);
|
|
1010
|
+
},
|
|
1011
|
+
});
|
|
1012
|
+
Object.defineProperty(base, 'firstNamedChild', {
|
|
1013
|
+
configurable: true,
|
|
1014
|
+
get() {
|
|
1015
|
+
const list = cachedList(this);
|
|
1016
|
+
return list === null ? nativeFirst.call(this) : (list.length > 0 ? list[0] : null);
|
|
1017
|
+
},
|
|
1018
|
+
});
|
|
1019
|
+
Object.defineProperty(base, 'lastNamedChild', {
|
|
1020
|
+
configurable: true,
|
|
1021
|
+
get() {
|
|
1022
|
+
const list = cachedList(this);
|
|
1023
|
+
return list === null ? nativeLast.call(this) : (list.length > 0 ? list[list.length - 1] : null);
|
|
1024
|
+
},
|
|
1025
|
+
});
|
|
1026
|
+
}
|
|
1027
|
+
const nativeField = base.childForFieldName;
|
|
1028
|
+
if (typeof nativeField === 'function') {
|
|
1029
|
+
Object.defineProperty(base, 'childForFieldName', {
|
|
1030
|
+
configurable: true,
|
|
1031
|
+
writable: true,
|
|
1032
|
+
value(fieldName) {
|
|
1033
|
+
const facts = nodeFactsOf(this);
|
|
1034
|
+
let fields = facts.fields;
|
|
1035
|
+
if (fields === null) {
|
|
1036
|
+
fields = new Map();
|
|
1037
|
+
facts.fields = fields;
|
|
1038
|
+
} else if (fields.has(fieldName)) {
|
|
1039
|
+
return fields.get(fieldName);
|
|
1040
|
+
}
|
|
1041
|
+
const child = nativeField.call(this, fieldName);
|
|
1042
|
+
fields.set(fieldName, child);
|
|
1043
|
+
return child;
|
|
1044
|
+
},
|
|
1045
|
+
});
|
|
1046
|
+
}
|
|
1047
|
+
} catch { /* stock accessors keep working */ }
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
/**
|
|
1051
|
+
* Make SyntaxNode#parent a native call only once per node wrapper.
|
|
1052
|
+
*
|
|
1053
|
+
* The binding returns one wrapper object per node of a tree (its node cache),
|
|
1054
|
+
* trees are never edited here, and a node's parent is immutable, so the first
|
|
1055
|
+
* native read is stored on the wrapper and later reads are property hits.
|
|
1056
|
+
* Enclosing-scope helpers climb to the translation unit from every call
|
|
1057
|
+
* site and declaration; on C++ headers those climbs were the single largest
|
|
1058
|
+
* native-bridge cost of a cold build (fix #385).
|
|
1059
|
+
*/
|
|
1060
|
+
function _installNodeParentCache(TS) {
|
|
1061
|
+
try {
|
|
1062
|
+
const base = TS?.SyntaxNode?.prototype;
|
|
1063
|
+
const desc = base && Object.getOwnPropertyDescriptor(base, 'parent');
|
|
1064
|
+
if (!desc?.get || desc.set) return; // shape changed upstream: leave it alone
|
|
1065
|
+
const nativeGet = desc.get;
|
|
1066
|
+
Object.defineProperty(base, 'parent', {
|
|
1067
|
+
configurable: true,
|
|
1068
|
+
get() {
|
|
1069
|
+
const facts = nodeFactsOf(this);
|
|
1070
|
+
let parent = facts.parent;
|
|
1071
|
+
if (parent === UNREAD) {
|
|
1072
|
+
parent = nativeGet.call(this);
|
|
1073
|
+
facts.parent = parent;
|
|
1074
|
+
}
|
|
1075
|
+
return parent;
|
|
1076
|
+
},
|
|
1077
|
+
});
|
|
1078
|
+
} catch { /* stock getter keeps working */ }
|
|
1079
|
+
}
|
|
1080
|
+
|
|
364
1081
|
/**
|
|
365
1082
|
* Make SyntaxNode#type a cheap data-property read.
|
|
366
1083
|
*
|
|
@@ -412,14 +1129,14 @@ function getParser(language) {
|
|
|
412
1129
|
const config = LANGUAGES[language];
|
|
413
1130
|
|
|
414
1131
|
if (!config) {
|
|
415
|
-
throw new
|
|
1132
|
+
throw new UcnError(`Unsupported language: ${language}`);
|
|
416
1133
|
}
|
|
417
1134
|
|
|
418
1135
|
try {
|
|
419
1136
|
const lang = config.treeSitterModule();
|
|
420
1137
|
parser.setLanguage(lang);
|
|
421
1138
|
} catch (e) {
|
|
422
|
-
throw new
|
|
1139
|
+
throw new UcnError(
|
|
423
1140
|
`Failed to load tree-sitter grammar for ${language}.\n` +
|
|
424
1141
|
`Install with: npm install tree-sitter-${language}\n` +
|
|
425
1142
|
`Original error: ${e.message}`,
|
|
@@ -455,7 +1172,7 @@ function getLanguageAdapter(language) {
|
|
|
455
1172
|
if (adapters[language]) return adapters[language];
|
|
456
1173
|
const config = LANGUAGES[language];
|
|
457
1174
|
if (!config) {
|
|
458
|
-
throw new
|
|
1175
|
+
throw new UcnError(`Unsupported language: ${language}`);
|
|
459
1176
|
}
|
|
460
1177
|
adapters[language] = createLanguageAdapter(config);
|
|
461
1178
|
return adapters[language];
|
|
@@ -490,6 +1207,7 @@ function getSupportedLanguages() {
|
|
|
490
1207
|
// Default 1MB handles most files; can be overridden via UCN_BUFFER_SIZE env var
|
|
491
1208
|
const DEFAULT_BUFFER_SIZE = 1024 * 1024; // 1MB
|
|
492
1209
|
const MAX_BUFFER_SIZE = 64 * 1024 * 1024; // 64MB cap
|
|
1210
|
+
const SMALL_BUFFER_SIZE = 32 * 1024;
|
|
493
1211
|
|
|
494
1212
|
const PARSE_OPTIONS = {
|
|
495
1213
|
bufferSize: parseInt(process.env.UCN_BUFFER_SIZE, 10) || DEFAULT_BUFFER_SIZE
|
|
@@ -532,9 +1250,13 @@ function safeParse(parser, content, oldTree = undefined, options = {}) {
|
|
|
532
1250
|
|
|
533
1251
|
const contentLength = content.length;
|
|
534
1252
|
|
|
535
|
-
// Try with escalating buffer sizes
|
|
1253
|
+
// Try with escalating buffer sizes. The first holds the content with
|
|
1254
|
+
// room to spare but no more: a 1MB buffer for a one-line probe or a
|
|
1255
|
+
// macro body costs 20x the parse itself (fix #396).
|
|
536
1256
|
const bufferSizes = [
|
|
537
|
-
parseInt(process.env.UCN_BUFFER_SIZE, 10) ||
|
|
1257
|
+
parseInt(process.env.UCN_BUFFER_SIZE, 10) ||
|
|
1258
|
+
Math.min(DEFAULT_BUFFER_SIZE, Math.max(SMALL_BUFFER_SIZE, contentLength * 2)),
|
|
1259
|
+
DEFAULT_BUFFER_SIZE,
|
|
538
1260
|
Math.max(DEFAULT_BUFFER_SIZE, contentLength * 2),
|
|
539
1261
|
Math.max(4 * 1024 * 1024, contentLength * 3),
|
|
540
1262
|
Math.max(16 * 1024 * 1024, contentLength * 4),
|