@descryy/adapter-csharp 0.1.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/dist/adapter.d.ts +31 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +263 -0
- package/dist/adapter.js.map +1 -0
- package/dist/aspnetcore.d.ts +207 -0
- package/dist/aspnetcore.d.ts.map +1 -0
- package/dist/aspnetcore.js +410 -0
- package/dist/aspnetcore.js.map +1 -0
- package/dist/client-base.d.ts +123 -0
- package/dist/client-base.d.ts.map +1 -0
- package/dist/client-base.js +123 -0
- package/dist/client-base.js.map +1 -0
- package/dist/client.d.ts +168 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +387 -0
- package/dist/client.js.map +1 -0
- package/dist/efcore.d.ts +198 -0
- package/dist/efcore.d.ts.map +1 -0
- package/dist/efcore.js +611 -0
- package/dist/efcore.js.map +1 -0
- package/dist/extract.d.ts +83 -0
- package/dist/extract.d.ts.map +1 -0
- package/dist/extract.js +1120 -0
- package/dist/extract.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/nullability.d.ts +117 -0
- package/dist/nullability.d.ts.map +1 -0
- package/dist/nullability.js +152 -0
- package/dist/nullability.js.map +1 -0
- package/dist/parse.d.ts +240 -0
- package/dist/parse.d.ts.map +1 -0
- package/dist/parse.js +861 -0
- package/dist/parse.js.map +1 -0
- package/dist/projects.d.ts +87 -0
- package/dist/projects.d.ts.map +1 -0
- package/dist/projects.js +226 -0
- package/dist/projects.js.map +1 -0
- package/dist/tree-sitter-c_sharp.wasm +0 -0
- package/package.json +35 -0
package/dist/parse.js
ADDED
|
@@ -0,0 +1,861 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One C# file, read into the shape the resolver wants.
|
|
3
|
+
*
|
|
4
|
+
* Hand-written walk, as in Java, Go, Rust and PHP, and for the same reason:
|
|
5
|
+
* containment is what a resolver needs and a query's match set is exactly what
|
|
6
|
+
* discards it.
|
|
7
|
+
*
|
|
8
|
+
* ## What C# changes
|
|
9
|
+
*
|
|
10
|
+
* Three things here are genuinely new work rather than a translation of an
|
|
11
|
+
* earlier language's rules.
|
|
12
|
+
*
|
|
13
|
+
* - **A type may be declared in several files.** `partial class` is the first
|
|
14
|
+
* construct in this project whose declaration is legitimately multi-file and
|
|
15
|
+
* says so in the source. DEC-083 settles the identity question; this reader
|
|
16
|
+
* only has to collect the parts.
|
|
17
|
+
* - **`using` imports a namespace, not a type.** PHP's `use` names one class
|
|
18
|
+
* and resolves in one step. C#'s brings in every type in a namespace, so a
|
|
19
|
+
* bare name is resolved by searching the enclosing namespaces outward and
|
|
20
|
+
* then each import — which means a name may be found in two places, and
|
|
21
|
+
* that is an ambiguity to disclose rather than a tie to break.
|
|
22
|
+
* - **The base list does not say which entry is the class.** DEC-084 decides
|
|
23
|
+
* it: at most one base class, and the language requires it first, so entries
|
|
24
|
+
* after the first are interfaces unconditionally and only one entry per
|
|
25
|
+
* class declaration ever needs resolving. The `IFoo` convention is not read.
|
|
26
|
+
*
|
|
27
|
+
* Nested types are ordinary here in a way they are not in PHP, so a type carries
|
|
28
|
+
* its nesting path rather than a single name.
|
|
29
|
+
*/
|
|
30
|
+
/** Keywords that name no user type. `var` is handled separately. */
|
|
31
|
+
const PREDEFINED = new Set([
|
|
32
|
+
"int", "uint", "long", "ulong", "short", "ushort", "byte", "sbyte",
|
|
33
|
+
"float", "double", "decimal", "bool", "char", "string", "object",
|
|
34
|
+
"void", "dynamic", "nint", "nuint", "var",
|
|
35
|
+
]);
|
|
36
|
+
/**
|
|
37
|
+
* The type a type expression names.
|
|
38
|
+
*
|
|
39
|
+
* `undefined` — none (predefined, `var`, or absent).
|
|
40
|
+
* `null` — a type is named but which one is ambiguous.
|
|
41
|
+
* `string` — the single type named, as written, generic arguments dropped.
|
|
42
|
+
*
|
|
43
|
+
* Generic arguments are dropped deliberately: `List<Order>` names `List`, and
|
|
44
|
+
* `Order` inside it is a separate reference the walker records on its own. A
|
|
45
|
+
* single collapsed name would attribute the collection's methods to its element.
|
|
46
|
+
*/
|
|
47
|
+
export function typeName(node) {
|
|
48
|
+
if (node === null || node === undefined)
|
|
49
|
+
return undefined;
|
|
50
|
+
switch (node.type) {
|
|
51
|
+
case "predefined_type":
|
|
52
|
+
case "implicit_type":
|
|
53
|
+
return undefined;
|
|
54
|
+
case "identifier": {
|
|
55
|
+
const text = node.text.trim();
|
|
56
|
+
return PREDEFINED.has(text) ? undefined : text;
|
|
57
|
+
}
|
|
58
|
+
case "qualified_name": {
|
|
59
|
+
const text = node.text.trim().replace(/<[^>]*>/g, "");
|
|
60
|
+
return text === "" ? undefined : text;
|
|
61
|
+
}
|
|
62
|
+
case "generic_name":
|
|
63
|
+
return typeName(node.namedChild(0));
|
|
64
|
+
case "nullable_type":
|
|
65
|
+
return typeName(node.childForFieldName("type") ?? node.namedChild(0));
|
|
66
|
+
case "array_type":
|
|
67
|
+
return typeName(node.childForFieldName("type") ?? node.namedChild(0));
|
|
68
|
+
case "pointer_type":
|
|
69
|
+
case "ref_type":
|
|
70
|
+
case "scoped_type":
|
|
71
|
+
return typeName(node.namedChild(0));
|
|
72
|
+
case "tuple_type":
|
|
73
|
+
return null;
|
|
74
|
+
default: {
|
|
75
|
+
if (node.namedChildCount === 1)
|
|
76
|
+
return typeName(node.namedChild(0));
|
|
77
|
+
if (node.namedChildCount === 0) {
|
|
78
|
+
const text = node.text.trim();
|
|
79
|
+
if (text === "" || PREDEFINED.has(text))
|
|
80
|
+
return undefined;
|
|
81
|
+
return /^[\w.]+$/.test(text) ? text : undefined;
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function hasModifier(node, keyword) {
|
|
88
|
+
for (let index = 0; index < node.childCount; index += 1) {
|
|
89
|
+
const child = node.child(index);
|
|
90
|
+
if (child?.type === "modifier" && child.text === keyword)
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* `[Fact]`, `[Theory]`, `[Xunit.Fact]` -> the last segment, **and its arguments**.
|
|
97
|
+
*
|
|
98
|
+
* The names alone were enough while `isTest` was the only consumer. A framework
|
|
99
|
+
* extractor needs the other half: `[Route("System/ActivityLog")]` collapses to
|
|
100
|
+
* `Route`, and the argument that is dropped **is the route**.
|
|
101
|
+
*
|
|
102
|
+
* That is the third instance of one shape in this adapter set — identity
|
|
103
|
+
* normalises, fidelity preserves, and both readings are correct for different
|
|
104
|
+
* consumers. The first two were a property's written type (`string` against
|
|
105
|
+
* `string?`) and a base list's generic argument
|
|
106
|
+
* (`IEntityTypeConfiguration<Order>`). Each collapse surfaced downstream as a
|
|
107
|
+
* clean zero rather than as an error, so both are carried side by side now.
|
|
108
|
+
*/
|
|
109
|
+
function attributeArgumentsOf(node) {
|
|
110
|
+
const found = {};
|
|
111
|
+
for (let index = 0; index < node.childCount; index += 1) {
|
|
112
|
+
const child = node.child(index);
|
|
113
|
+
if (child?.type !== "attribute_list")
|
|
114
|
+
continue;
|
|
115
|
+
for (let inner = 0; inner < child.namedChildCount; inner += 1) {
|
|
116
|
+
const attribute = child.namedChild(inner);
|
|
117
|
+
if (attribute?.type !== "attribute")
|
|
118
|
+
continue;
|
|
119
|
+
const name = attribute.childForFieldName("name")?.text.trim();
|
|
120
|
+
if (name === undefined)
|
|
121
|
+
continue;
|
|
122
|
+
const bare = name.replace(/<[^>]*>/g, "");
|
|
123
|
+
const simple = bare.slice(bare.lastIndexOf(".") + 1);
|
|
124
|
+
// The first string literal argument, as written. A non-literal argument
|
|
125
|
+
// yields nothing rather than its source text: a path assembled at runtime
|
|
126
|
+
// is not a path this adapter can read, and DEC-128 refuses it.
|
|
127
|
+
const literal = /\(\s*"([^"]*)"/.exec(attribute.text);
|
|
128
|
+
if (literal !== null && found[simple] === undefined)
|
|
129
|
+
found[simple] = literal[1];
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return found;
|
|
133
|
+
}
|
|
134
|
+
function attributesOf(node) {
|
|
135
|
+
const names = [];
|
|
136
|
+
for (let index = 0; index < node.childCount; index += 1) {
|
|
137
|
+
const child = node.child(index);
|
|
138
|
+
if (child?.type !== "attribute_list")
|
|
139
|
+
continue;
|
|
140
|
+
for (let inner = 0; inner < child.namedChildCount; inner += 1) {
|
|
141
|
+
const attribute = child.namedChild(inner);
|
|
142
|
+
if (attribute?.type !== "attribute")
|
|
143
|
+
continue;
|
|
144
|
+
const name = attribute.childForFieldName("name")?.text.trim();
|
|
145
|
+
if (name === undefined)
|
|
146
|
+
continue;
|
|
147
|
+
const bare = name.replace(/<[^>]*>/g, "");
|
|
148
|
+
names.push(bare.slice(bare.lastIndexOf(".") + 1));
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return names;
|
|
152
|
+
}
|
|
153
|
+
function describeReceiver(node) {
|
|
154
|
+
if (node === null || node === undefined)
|
|
155
|
+
return { written: undefined };
|
|
156
|
+
switch (node.type) {
|
|
157
|
+
case "identifier":
|
|
158
|
+
case "predefined_type":
|
|
159
|
+
return { written: node.text.trim() };
|
|
160
|
+
case "this_expression":
|
|
161
|
+
return { written: "this" };
|
|
162
|
+
case "base_expression":
|
|
163
|
+
return { written: "base" };
|
|
164
|
+
case "generic_name":
|
|
165
|
+
return { written: node.namedChild(0)?.text.trim() };
|
|
166
|
+
case "qualified_name":
|
|
167
|
+
return { written: node.text.trim() };
|
|
168
|
+
case "parenthesized_expression":
|
|
169
|
+
return describeReceiver(node.namedChild(0));
|
|
170
|
+
case "object_creation_expression": {
|
|
171
|
+
// `new Order(x).Total()` — the type is at the site.
|
|
172
|
+
const named = typeName(node.childForFieldName("type"));
|
|
173
|
+
return typeof named === "string" ? { written: undefined, type: named } : { written: undefined, opaque: true };
|
|
174
|
+
}
|
|
175
|
+
case "cast_expression": {
|
|
176
|
+
// `((IRepository)x).FindById()` — also written at the site.
|
|
177
|
+
const named = typeName(node.childForFieldName("type"));
|
|
178
|
+
return typeof named === "string" ? { written: undefined, type: named } : { written: undefined, opaque: true };
|
|
179
|
+
}
|
|
180
|
+
case "conditional_access_expression": {
|
|
181
|
+
// `x?.y.z()` and `a?[0].F()`. The null-conditional changes only whether the
|
|
182
|
+
// chain is evaluated, never what it denotes: `x?.y` names exactly what
|
|
183
|
+
// `x.y` names. Unwrapping it here is what lets the ordinary member-access
|
|
184
|
+
// machinery below do the work; falling through to `default` made every
|
|
185
|
+
// such receiver opaque, and before the flag above existed, implicit.
|
|
186
|
+
const object = describeReceiver(node.namedChild(0));
|
|
187
|
+
const binding = node.namedChild(1);
|
|
188
|
+
if (binding?.type !== "member_binding_expression") {
|
|
189
|
+
// `a?[0]` is an indexer. The element type is not written at the site.
|
|
190
|
+
return { written: undefined, opaque: true };
|
|
191
|
+
}
|
|
192
|
+
const member = binding.namedChild(0);
|
|
193
|
+
const memberName = (member?.type === "generic_name" ? member.namedChild(0)?.text : member?.text)?.trim();
|
|
194
|
+
if (memberName === undefined || object.written === undefined)
|
|
195
|
+
return { written: undefined, opaque: true };
|
|
196
|
+
return {
|
|
197
|
+
written: `${object.written}.${memberName}`,
|
|
198
|
+
property: { of: object.written, name: memberName },
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
case "member_access_expression": {
|
|
202
|
+
// `this._repository.FindById()` — the type is on the member declaration,
|
|
203
|
+
// but `Shop.Orders.Money.Format()` is a *namespace* path, not a member
|
|
204
|
+
// chain. Both are `member_access_expression`, and telling them apart needs
|
|
205
|
+
// the resolver, so the whole written path is handed over and the resolver
|
|
206
|
+
// tries it as a qualified name first.
|
|
207
|
+
const object = describeReceiver(node.childForFieldName("expression"));
|
|
208
|
+
const member = node.childForFieldName("name");
|
|
209
|
+
if (member === null)
|
|
210
|
+
return { written: undefined, opaque: true };
|
|
211
|
+
const memberName = member.type === "generic_name" ? member.namedChild(0)?.text : member.text;
|
|
212
|
+
if (memberName === undefined || object.written === undefined) {
|
|
213
|
+
// The object could not be written down, so neither can the member path.
|
|
214
|
+
// Opaque rather than absent — see `Receiver.opaque`.
|
|
215
|
+
return object.type === undefined && object.call === undefined
|
|
216
|
+
? { written: undefined, opaque: true }
|
|
217
|
+
: { written: undefined };
|
|
218
|
+
}
|
|
219
|
+
return {
|
|
220
|
+
written: `${object.written}.${memberName.trim()}`,
|
|
221
|
+
property: { of: object.written, name: memberName.trim() },
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
case "invocation_expression": {
|
|
225
|
+
const call = callSignature(node);
|
|
226
|
+
return call === undefined ? { written: undefined, opaque: true } : { written: undefined, call };
|
|
227
|
+
}
|
|
228
|
+
default:
|
|
229
|
+
// A binary expression, a literal, a lambda, a `switch` expression — a
|
|
230
|
+
// receiver is written and this reader cannot say what it denotes.
|
|
231
|
+
return { written: undefined, opaque: true };
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
/** The `(receiver, name)` an invocation names, when both are written. */
|
|
235
|
+
function callSignature(node) {
|
|
236
|
+
const callee = node.childForFieldName("function");
|
|
237
|
+
if (callee === null)
|
|
238
|
+
return undefined;
|
|
239
|
+
if (callee.type === "identifier")
|
|
240
|
+
return { receiver: undefined, name: callee.text.trim() };
|
|
241
|
+
if (callee.type === "generic_name") {
|
|
242
|
+
const bare = callee.namedChild(0)?.text.trim();
|
|
243
|
+
return bare === undefined ? undefined : { receiver: undefined, name: bare };
|
|
244
|
+
}
|
|
245
|
+
if (callee.type === "member_access_expression") {
|
|
246
|
+
const member = callee.childForFieldName("name");
|
|
247
|
+
if (member === null)
|
|
248
|
+
return undefined;
|
|
249
|
+
const name = (member.type === "generic_name" ? member.namedChild(0)?.text : member.text)?.trim();
|
|
250
|
+
if (name === undefined)
|
|
251
|
+
return undefined;
|
|
252
|
+
const receiver = describeReceiver(callee.childForFieldName("expression"));
|
|
253
|
+
if (receiver.written === undefined)
|
|
254
|
+
return undefined;
|
|
255
|
+
return { receiver: receiver.written, name };
|
|
256
|
+
}
|
|
257
|
+
return undefined;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* The first argument of an invocation, only when it is a bare string literal.
|
|
261
|
+
*
|
|
262
|
+
* Mirrors Java's `firstStringArgumentOf` exactly: `argument_list`'s first
|
|
263
|
+
* named child is an `argument`, whose own first named child is the value —
|
|
264
|
+
* `string_literal` when the call site wrote one, an `identifier` or anything
|
|
265
|
+
* else otherwise. `"abc"` -> `abc`; a concatenation or interpolated string
|
|
266
|
+
* (`$"..."`, `interpolated_string_expression`) returns `undefined` rather than
|
|
267
|
+
* the fragment it can see, the same refusal-not-guess DEC-127 already applies
|
|
268
|
+
* to Java's read of the identical shape.
|
|
269
|
+
*/
|
|
270
|
+
function firstStringArgumentOf(invocation) {
|
|
271
|
+
const args = invocation.childForFieldName("arguments");
|
|
272
|
+
if (args === null)
|
|
273
|
+
return undefined;
|
|
274
|
+
const first = args.namedChild(0)?.namedChild(0);
|
|
275
|
+
if (first === null || first === undefined || first.type !== "string_literal")
|
|
276
|
+
return undefined;
|
|
277
|
+
return first.text.slice(1, -1);
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* The first argument's raw text, whatever shape it is. Mirrors Java's
|
|
281
|
+
* `firstArgumentTextOf`. See {@link CsRef.firstArgumentText}.
|
|
282
|
+
*/
|
|
283
|
+
function firstArgumentTextOf(invocation) {
|
|
284
|
+
const args = invocation.childForFieldName("arguments");
|
|
285
|
+
if (args === null)
|
|
286
|
+
return undefined;
|
|
287
|
+
const first = args.namedChild(0)?.namedChild(0);
|
|
288
|
+
return first === null || first === undefined ? undefined : first.text;
|
|
289
|
+
}
|
|
290
|
+
function pushInvocation(scope, node) {
|
|
291
|
+
const callee = node.childForFieldName("function");
|
|
292
|
+
if (callee === null)
|
|
293
|
+
return;
|
|
294
|
+
if (callee.type === "identifier" || callee.type === "generic_name") {
|
|
295
|
+
const name = (callee.type === "generic_name" ? callee.namedChild(0)?.text : callee.text)?.trim();
|
|
296
|
+
if (name === undefined)
|
|
297
|
+
return;
|
|
298
|
+
scope.refs.push({
|
|
299
|
+
kind: "call",
|
|
300
|
+
name,
|
|
301
|
+
receiver: undefined,
|
|
302
|
+
line: node.startPosition.row + 1,
|
|
303
|
+
raw: node.text.slice(0, 200),
|
|
304
|
+
});
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
if (callee.type === "member_access_expression" || callee.type === "conditional_access_expression") {
|
|
308
|
+
const holder = callee.type === "member_access_expression" ? callee : callee.namedChild(0);
|
|
309
|
+
const member = callee.type === "member_access_expression"
|
|
310
|
+
? callee.childForFieldName("name")
|
|
311
|
+
: callee.namedChild(1)?.namedChild(0);
|
|
312
|
+
if (holder === null || member === null || member === undefined)
|
|
313
|
+
return;
|
|
314
|
+
const name = (member.type === "generic_name" ? member.namedChild(0)?.text : member.text)?.trim();
|
|
315
|
+
if (name === undefined)
|
|
316
|
+
return;
|
|
317
|
+
const receiver = describeReceiver(callee.type === "member_access_expression" ? callee.childForFieldName("expression") : holder);
|
|
318
|
+
// A receiver was written and could not be read. Disclosing that is the whole
|
|
319
|
+
// difference between a gap and a wrong answer: falling through with
|
|
320
|
+
// `receiver: undefined` makes `emitCall` treat it as an implicit `self`.
|
|
321
|
+
if (receiver.opaque === true) {
|
|
322
|
+
scope.refs.push({
|
|
323
|
+
kind: "dynamic",
|
|
324
|
+
name: callee.text.slice(0, 60),
|
|
325
|
+
receiver: undefined,
|
|
326
|
+
line: node.startPosition.row + 1,
|
|
327
|
+
raw: node.text.slice(0, 200),
|
|
328
|
+
});
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
const literal = firstStringArgumentOf(node);
|
|
332
|
+
const argumentText = firstArgumentTextOf(node);
|
|
333
|
+
scope.refs.push({
|
|
334
|
+
kind: "call",
|
|
335
|
+
name,
|
|
336
|
+
receiver: receiver.written,
|
|
337
|
+
...(receiver.type === undefined ? {} : { receiverType: receiver.type }),
|
|
338
|
+
...(receiver.property === undefined ? {} : { receiverProperty: receiver.property }),
|
|
339
|
+
...(receiver.call === undefined ? {} : { receiverCall: receiver.call }),
|
|
340
|
+
line: node.startPosition.row + 1,
|
|
341
|
+
raw: node.text.slice(0, 200),
|
|
342
|
+
// Spread rather than assigned — `exactOptionalPropertyTypes` distinguishes
|
|
343
|
+
// absent from present-and-undefined, and the distinction is the point:
|
|
344
|
+
// absent means "not a call worth reading further," present-and-undefined
|
|
345
|
+
// would wrongly mean "a call whose first argument is nothing."
|
|
346
|
+
...(literal === undefined ? {} : { firstStringArgument: literal }),
|
|
347
|
+
...(argumentText === undefined ? {} : { firstArgumentText: argumentText }),
|
|
348
|
+
});
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
// `handler(payload)` where `handler` is a delegate field, `_map[key](x)`,
|
|
352
|
+
// `((Func<int,int>)f)(x)`. The target is a value; which method it holds is a
|
|
353
|
+
// runtime fact. Golden pattern 12.
|
|
354
|
+
scope.refs.push({
|
|
355
|
+
kind: "dynamic",
|
|
356
|
+
name: callee.text.slice(0, 60),
|
|
357
|
+
receiver: undefined,
|
|
358
|
+
line: node.startPosition.row + 1,
|
|
359
|
+
raw: node.text.slice(0, 200),
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* A `variable_declarator`'s own initialiser, whether or not the declaration's
|
|
364
|
+
* type was written explicitly — `recordDeclarator` only needs this for `var`,
|
|
365
|
+
* but a field's own inline initialiser (`private readonly HttpClient _client
|
|
366
|
+
* = factory.CreateClient();`) matters for D-FIX-3's client-base extractor
|
|
367
|
+
* (DEC-164) even when the field's type is written explicitly, so this is a
|
|
368
|
+
* standalone reader rather than folded into `recordDeclarator`'s own `var`
|
|
369
|
+
* branch.
|
|
370
|
+
*/
|
|
371
|
+
function initialiserOf(declarator) {
|
|
372
|
+
let initialiser;
|
|
373
|
+
for (let inner = 0; inner < declarator.namedChildCount; inner += 1) {
|
|
374
|
+
const child = declarator.namedChild(inner);
|
|
375
|
+
if (child !== null && child.type !== "bracketed_argument_list")
|
|
376
|
+
initialiser = child;
|
|
377
|
+
}
|
|
378
|
+
return initialiser;
|
|
379
|
+
}
|
|
380
|
+
/** `var x = new Order();` and `var x = Build();` — bind the local's type. */
|
|
381
|
+
function recordDeclarator(scope, declaration) {
|
|
382
|
+
const written = typeName(declaration.childForFieldName("type"));
|
|
383
|
+
for (let index = 0; index < declaration.namedChildCount; index += 1) {
|
|
384
|
+
const declarator = declaration.namedChild(index);
|
|
385
|
+
if (declarator?.type !== "variable_declarator")
|
|
386
|
+
continue;
|
|
387
|
+
const name = declarator.childForFieldName("name")?.text.trim();
|
|
388
|
+
if (name === undefined)
|
|
389
|
+
continue;
|
|
390
|
+
if (typeof written === "string") {
|
|
391
|
+
scope.locals.set(name, written);
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
// `var` — the type is in the initialiser, which is still source.
|
|
395
|
+
const initialiser = initialiserOf(declarator);
|
|
396
|
+
if (initialiser === undefined) {
|
|
397
|
+
scope.locals.set(name, written ?? null);
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
400
|
+
if (initialiser.type === "object_creation_expression") {
|
|
401
|
+
const created = typeName(initialiser.childForFieldName("type"));
|
|
402
|
+
scope.locals.set(name, typeof created === "string" ? created : null);
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
if (initialiser.type === "invocation_expression") {
|
|
406
|
+
const call = callSignature(initialiser);
|
|
407
|
+
if (call !== undefined) {
|
|
408
|
+
scope.pending.set(name, call);
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
scope.locals.set(name, null);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
function readParameters(node, scope, members) {
|
|
416
|
+
if (node === null || node === undefined)
|
|
417
|
+
return;
|
|
418
|
+
for (let index = 0; index < node.namedChildCount; index += 1) {
|
|
419
|
+
const parameter = node.namedChild(index);
|
|
420
|
+
if (parameter?.type !== "parameter")
|
|
421
|
+
continue;
|
|
422
|
+
const name = parameter.childForFieldName("name")?.text.trim();
|
|
423
|
+
const declared = typeName(parameter.childForFieldName("type"));
|
|
424
|
+
if (name !== undefined) {
|
|
425
|
+
scope.locals.set(name, declared ?? null);
|
|
426
|
+
scope.parameterNames.add(name);
|
|
427
|
+
}
|
|
428
|
+
if (typeof declared === "string") {
|
|
429
|
+
scope.refs.push({
|
|
430
|
+
kind: "type",
|
|
431
|
+
name: declared,
|
|
432
|
+
receiver: undefined,
|
|
433
|
+
line: parameter.startPosition.row + 1,
|
|
434
|
+
raw: parameter.text.slice(0, 160),
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
// A primary constructor on a `class`/`record` declares members, and this is
|
|
438
|
+
// the only place their types are written.
|
|
439
|
+
if (members !== undefined && name !== undefined)
|
|
440
|
+
members.set(name, declared ?? null);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
function walkBody(node, scope) {
|
|
444
|
+
const stack = [node];
|
|
445
|
+
while (stack.length > 0) {
|
|
446
|
+
const current = stack.pop();
|
|
447
|
+
if (current === undefined)
|
|
448
|
+
continue;
|
|
449
|
+
switch (current.type) {
|
|
450
|
+
case "variable_declaration":
|
|
451
|
+
recordDeclarator(scope, current);
|
|
452
|
+
break;
|
|
453
|
+
case "invocation_expression":
|
|
454
|
+
pushInvocation(scope, current);
|
|
455
|
+
break;
|
|
456
|
+
case "assignment_expression": {
|
|
457
|
+
// `Client = factory.CreateClient(...)` and `this.Client = ...` — only
|
|
458
|
+
// D-FIX-3's client-base extractor (DEC-164) reads this, and only from
|
|
459
|
+
// a `.ctor`'s own scope (extract.ts's choice, not this reader's). The
|
|
460
|
+
// invocation itself is walked normally regardless (it is still a
|
|
461
|
+
// child of this node) and produces its own `CsRef`; this case exists
|
|
462
|
+
// purely to remember *which name the result was bound to*.
|
|
463
|
+
const left = current.childForFieldName("left");
|
|
464
|
+
const right = current.childForFieldName("right");
|
|
465
|
+
if (left !== null && right !== null && right.type === "invocation_expression") {
|
|
466
|
+
let bound;
|
|
467
|
+
if (left.type === "identifier")
|
|
468
|
+
bound = left.text.trim();
|
|
469
|
+
else if (left.type === "member_access_expression") {
|
|
470
|
+
const receiver = left.childForFieldName("expression")?.text.trim();
|
|
471
|
+
if (receiver === "this")
|
|
472
|
+
bound = left.childForFieldName("name")?.text.trim();
|
|
473
|
+
}
|
|
474
|
+
if (bound !== undefined) {
|
|
475
|
+
const call = callSignature(right);
|
|
476
|
+
if (call !== undefined)
|
|
477
|
+
scope.propertyAssignments.set(bound, call);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
break;
|
|
481
|
+
}
|
|
482
|
+
case "object_creation_expression": {
|
|
483
|
+
const created = typeName(current.childForFieldName("type"));
|
|
484
|
+
if (typeof created === "string") {
|
|
485
|
+
scope.refs.push({
|
|
486
|
+
kind: "new",
|
|
487
|
+
name: created,
|
|
488
|
+
receiver: undefined,
|
|
489
|
+
line: current.startPosition.row + 1,
|
|
490
|
+
raw: current.text.slice(0, 200),
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
break;
|
|
494
|
+
}
|
|
495
|
+
case "declaration_pattern": {
|
|
496
|
+
// `if (item is Folder folder)` and `case Folder folder:` — a pattern
|
|
497
|
+
// that BINDS a local whose type is written immediately before it. Every
|
|
498
|
+
// call through that local was previously disclosed as an untyped
|
|
499
|
+
// receiver, which was honest but wasteful: the type is right there.
|
|
500
|
+
// jellyfin's `MetadataService.GetChildrenForMetadataUpdates` is the
|
|
501
|
+
// canonical shape, and it is idiomatic modern C# rather than a corner.
|
|
502
|
+
const declared = typeName(current.namedChild(0));
|
|
503
|
+
const bound = current.namedChild(1)?.text.trim();
|
|
504
|
+
if (bound !== undefined)
|
|
505
|
+
scope.locals.set(bound, typeof declared === "string" ? declared : null);
|
|
506
|
+
if (typeof declared === "string") {
|
|
507
|
+
scope.refs.push({
|
|
508
|
+
kind: "type",
|
|
509
|
+
name: declared,
|
|
510
|
+
receiver: undefined,
|
|
511
|
+
line: current.startPosition.row + 1,
|
|
512
|
+
raw: current.text.slice(0, 120),
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
break;
|
|
516
|
+
}
|
|
517
|
+
case "catch_declaration": {
|
|
518
|
+
const caught = typeName(current.childForFieldName("type") ?? current.namedChild(0));
|
|
519
|
+
const bound = current.childForFieldName("name")?.text.trim();
|
|
520
|
+
if (bound !== undefined)
|
|
521
|
+
scope.locals.set(bound, typeof caught === "string" ? caught : null);
|
|
522
|
+
if (typeof caught === "string") {
|
|
523
|
+
scope.refs.push({
|
|
524
|
+
kind: "type",
|
|
525
|
+
name: caught,
|
|
526
|
+
receiver: undefined,
|
|
527
|
+
line: current.startPosition.row + 1,
|
|
528
|
+
raw: current.text.slice(0, 120),
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
break;
|
|
532
|
+
}
|
|
533
|
+
case "member_access_expression": {
|
|
534
|
+
// `Status.Pending`, `Order.Empty` — a type referenced as a value, which
|
|
535
|
+
// is golden pattern 13. Only recorded when the holder is a bare name;
|
|
536
|
+
// the resolver decides whether that name is a type or a variable, and
|
|
537
|
+
// declines when it is neither.
|
|
538
|
+
const holder = current.childForFieldName("expression");
|
|
539
|
+
if (holder !== null && (holder.type === "identifier" || holder.type === "qualified_name")) {
|
|
540
|
+
scope.refs.push({
|
|
541
|
+
kind: "type",
|
|
542
|
+
name: holder.text.trim(),
|
|
543
|
+
receiver: undefined,
|
|
544
|
+
speculative: true,
|
|
545
|
+
line: current.startPosition.row + 1,
|
|
546
|
+
raw: current.text.slice(0, 120),
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
break;
|
|
550
|
+
}
|
|
551
|
+
default:
|
|
552
|
+
break;
|
|
553
|
+
}
|
|
554
|
+
for (let index = 0; index < current.namedChildCount; index += 1) {
|
|
555
|
+
const child = current.namedChild(index);
|
|
556
|
+
if (child !== null)
|
|
557
|
+
stack.push(child);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
function readFunction(node, ownerPath, name) {
|
|
562
|
+
const scope = { refs: [], locals: new Map(), pending: new Map(), parameterNames: new Set(), propertyAssignments: new Map() };
|
|
563
|
+
readParameters(node.childForFieldName("parameters"), scope);
|
|
564
|
+
const returns = typeName(node.childForFieldName("returns"));
|
|
565
|
+
if (typeof returns === "string") {
|
|
566
|
+
scope.refs.push({
|
|
567
|
+
kind: "type",
|
|
568
|
+
name: returns,
|
|
569
|
+
receiver: undefined,
|
|
570
|
+
line: node.startPosition.row + 1,
|
|
571
|
+
raw: node.text.slice(0, 120),
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
const body = node.childForFieldName("body");
|
|
575
|
+
if (body !== null)
|
|
576
|
+
walkBody(body, scope);
|
|
577
|
+
return {
|
|
578
|
+
name,
|
|
579
|
+
ownerPath,
|
|
580
|
+
isStatic: hasModifier(node, "static"),
|
|
581
|
+
isAbstract: hasModifier(node, "abstract"),
|
|
582
|
+
attributes: attributesOf(node),
|
|
583
|
+
attributeArguments: attributeArgumentsOf(node),
|
|
584
|
+
returns,
|
|
585
|
+
startLine: node.startPosition.row + 1,
|
|
586
|
+
endLine: node.endPosition.row + 1,
|
|
587
|
+
scope,
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
/** The base list, **in written order** — DEC-084 reads position as evidence. */
|
|
591
|
+
function baseListOf(node) {
|
|
592
|
+
const names = [];
|
|
593
|
+
const written = [];
|
|
594
|
+
for (let index = 0; index < node.namedChildCount; index += 1) {
|
|
595
|
+
const child = node.namedChild(index);
|
|
596
|
+
if (child?.type !== "base_list")
|
|
597
|
+
continue;
|
|
598
|
+
for (let inner = 0; inner < child.namedChildCount; inner += 1) {
|
|
599
|
+
const entry = child.namedChild(inner);
|
|
600
|
+
if (entry === null || entry.type === "argument_list")
|
|
601
|
+
continue;
|
|
602
|
+
const source = entry.type === "primary_constructor_base_type"
|
|
603
|
+
? (entry.childForFieldName("type") ?? entry.namedChild(0))
|
|
604
|
+
: entry;
|
|
605
|
+
const named = typeName(source);
|
|
606
|
+
if (typeof named === "string") {
|
|
607
|
+
names.push(named);
|
|
608
|
+
written.push(source?.text.trim() ?? named);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
return { names, written };
|
|
613
|
+
}
|
|
614
|
+
const FORMS = {
|
|
615
|
+
class_declaration: "class",
|
|
616
|
+
interface_declaration: "interface",
|
|
617
|
+
struct_declaration: "struct",
|
|
618
|
+
record_declaration: "record",
|
|
619
|
+
record_struct_declaration: "record",
|
|
620
|
+
enum_declaration: "enum",
|
|
621
|
+
};
|
|
622
|
+
export function readFile(root, file, hasError) {
|
|
623
|
+
let namespaceName = "";
|
|
624
|
+
const usings = [];
|
|
625
|
+
const types = [];
|
|
626
|
+
const funcs = [];
|
|
627
|
+
const readUsing = (node) => {
|
|
628
|
+
const alias = node.childForFieldName("name")?.text.trim();
|
|
629
|
+
let target;
|
|
630
|
+
for (let index = 0; index < node.namedChildCount; index += 1) {
|
|
631
|
+
const child = node.namedChild(index);
|
|
632
|
+
if (child === null)
|
|
633
|
+
continue;
|
|
634
|
+
if (child.type === "identifier" && child.text.trim() === alias)
|
|
635
|
+
continue;
|
|
636
|
+
if (child.type === "qualified_name" || child.type === "identifier" || child.type === "generic_name") {
|
|
637
|
+
target = child.text.trim();
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
if (target === undefined)
|
|
641
|
+
return;
|
|
642
|
+
usings.push({
|
|
643
|
+
target,
|
|
644
|
+
alias,
|
|
645
|
+
isStatic: /\busing\s+static\b/.test(node.text),
|
|
646
|
+
isGlobal: /^\s*global\b/.test(node.text),
|
|
647
|
+
line: node.startPosition.row + 1,
|
|
648
|
+
});
|
|
649
|
+
};
|
|
650
|
+
const readType = (node, form, enclosing) => {
|
|
651
|
+
const name = node.childForFieldName("name")?.text.trim();
|
|
652
|
+
if (name === undefined)
|
|
653
|
+
return;
|
|
654
|
+
const path = [...enclosing, name];
|
|
655
|
+
const members = new Map();
|
|
656
|
+
const properties = [];
|
|
657
|
+
const typeRefs = [];
|
|
658
|
+
const memberProvenance = new Map();
|
|
659
|
+
// A primary constructor's parameters are members, and their types are
|
|
660
|
+
// written nowhere else: `record OrderRecord(string Id, Status Status)`.
|
|
661
|
+
const primary = node.childForFieldName("parameters");
|
|
662
|
+
if (primary !== null) {
|
|
663
|
+
const holder = { refs: typeRefs, locals: new Map(), pending: new Map(), parameterNames: new Set(), propertyAssignments: new Map() };
|
|
664
|
+
readParameters(primary, holder, members);
|
|
665
|
+
}
|
|
666
|
+
for (let index = 0; index < node.namedChildCount; index += 1) {
|
|
667
|
+
const child = node.namedChild(index);
|
|
668
|
+
if (child?.type === "parameter_list") {
|
|
669
|
+
const holder = { refs: typeRefs, locals: new Map(), pending: new Map(), parameterNames: new Set(), propertyAssignments: new Map() };
|
|
670
|
+
readParameters(child, holder, members);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
const body = node.childForFieldName("body");
|
|
674
|
+
if (body !== null) {
|
|
675
|
+
for (let index = 0; index < body.namedChildCount; index += 1) {
|
|
676
|
+
const member = body.namedChild(index);
|
|
677
|
+
if (member === null)
|
|
678
|
+
continue;
|
|
679
|
+
const nested = FORMS[member.type];
|
|
680
|
+
if (nested !== undefined) {
|
|
681
|
+
readType(member, nested, path);
|
|
682
|
+
continue;
|
|
683
|
+
}
|
|
684
|
+
if (member.type === "method_declaration") {
|
|
685
|
+
const method = member.childForFieldName("name")?.text.trim();
|
|
686
|
+
if (method !== undefined)
|
|
687
|
+
funcs.push(readFunction(member, path, method));
|
|
688
|
+
continue;
|
|
689
|
+
}
|
|
690
|
+
if (member.type === "constructor_declaration") {
|
|
691
|
+
funcs.push(readFunction(member, path, ".ctor"));
|
|
692
|
+
continue;
|
|
693
|
+
}
|
|
694
|
+
if (member.type === "property_declaration") {
|
|
695
|
+
const property = member.childForFieldName("name")?.text.trim();
|
|
696
|
+
const declared = typeName(member.childForFieldName("type"));
|
|
697
|
+
if (property !== undefined)
|
|
698
|
+
members.set(property, declared ?? null);
|
|
699
|
+
if (property !== undefined) {
|
|
700
|
+
const accessorList = member.childForFieldName("accessors");
|
|
701
|
+
properties.push({
|
|
702
|
+
name: property,
|
|
703
|
+
// Raw text, because `string` and `string?` share a base name and
|
|
704
|
+
// have opposite nullability. See {@link CsProperty}.
|
|
705
|
+
written: member.childForFieldName("type")?.text.trim() ?? "",
|
|
706
|
+
declared,
|
|
707
|
+
isPublic: hasModifier(member, "public"),
|
|
708
|
+
// An expression-bodied property (`=> x`) is a getter with no
|
|
709
|
+
// accessor list, so both forms count.
|
|
710
|
+
hasGetter: member.childForFieldName("value") !== null ||
|
|
711
|
+
(accessorList?.text.includes("get") ?? false),
|
|
712
|
+
attributes: attributesOf(member),
|
|
713
|
+
line: member.startPosition.row + 1,
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
if (typeof declared === "string") {
|
|
717
|
+
typeRefs.push({
|
|
718
|
+
kind: "type",
|
|
719
|
+
name: declared,
|
|
720
|
+
receiver: undefined,
|
|
721
|
+
line: member.startPosition.row + 1,
|
|
722
|
+
raw: member.text.slice(0, 160),
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
// A property with a body is a function as well as a member.
|
|
726
|
+
const accessors = member.childForFieldName("accessors");
|
|
727
|
+
if (accessors !== null) {
|
|
728
|
+
const holder = { refs: typeRefs, locals: new Map(), pending: new Map(), parameterNames: new Set(), propertyAssignments: new Map() };
|
|
729
|
+
walkBody(accessors, holder);
|
|
730
|
+
}
|
|
731
|
+
const arrow = member.childForFieldName("value");
|
|
732
|
+
if (arrow !== null) {
|
|
733
|
+
const holder = { refs: typeRefs, locals: new Map(), pending: new Map(), parameterNames: new Set(), propertyAssignments: new Map() };
|
|
734
|
+
walkBody(arrow, holder);
|
|
735
|
+
}
|
|
736
|
+
continue;
|
|
737
|
+
}
|
|
738
|
+
if (member.type === "field_declaration") {
|
|
739
|
+
for (let inner = 0; inner < member.namedChildCount; inner += 1) {
|
|
740
|
+
const declaration = member.namedChild(inner);
|
|
741
|
+
if (declaration?.type !== "variable_declaration")
|
|
742
|
+
continue;
|
|
743
|
+
const declared = typeName(declaration.childForFieldName("type"));
|
|
744
|
+
for (let each = 0; each < declaration.namedChildCount; each += 1) {
|
|
745
|
+
const declarator = declaration.namedChild(each);
|
|
746
|
+
if (declarator?.type !== "variable_declarator")
|
|
747
|
+
continue;
|
|
748
|
+
const field = declarator.childForFieldName("name")?.text.trim();
|
|
749
|
+
if (field !== undefined)
|
|
750
|
+
members.set(field, declared ?? null);
|
|
751
|
+
// D-FIX-3's client-base extractor (DEC-164): a field's own
|
|
752
|
+
// inline initialiser, when it is a traceable invocation.
|
|
753
|
+
// `CleanArchitecture`'s `private readonly HttpClient _client =
|
|
754
|
+
// factory.CreateClient();` is the measured shape.
|
|
755
|
+
if (field !== undefined) {
|
|
756
|
+
const initialiser = initialiserOf(declarator);
|
|
757
|
+
if (initialiser?.type === "invocation_expression") {
|
|
758
|
+
const call = callSignature(initialiser);
|
|
759
|
+
if (call !== undefined)
|
|
760
|
+
memberProvenance.set(field, call);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
if (typeof declared === "string") {
|
|
765
|
+
typeRefs.push({
|
|
766
|
+
kind: "type",
|
|
767
|
+
name: declared,
|
|
768
|
+
receiver: undefined,
|
|
769
|
+
line: member.startPosition.row + 1,
|
|
770
|
+
raw: member.text.slice(0, 160),
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
const { names: baseList, written: baseListWritten } = baseListOf(node);
|
|
778
|
+
for (const supertype of baseList) {
|
|
779
|
+
typeRefs.push({
|
|
780
|
+
kind: "type",
|
|
781
|
+
name: supertype,
|
|
782
|
+
receiver: undefined,
|
|
783
|
+
line: node.startPosition.row + 1,
|
|
784
|
+
raw: supertype,
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
types.push({
|
|
788
|
+
name,
|
|
789
|
+
path,
|
|
790
|
+
form,
|
|
791
|
+
isPartial: hasModifier(node, "partial"),
|
|
792
|
+
isAbstract: hasModifier(node, "abstract"),
|
|
793
|
+
baseList,
|
|
794
|
+
baseListWritten,
|
|
795
|
+
members,
|
|
796
|
+
properties,
|
|
797
|
+
attributes: attributesOf(node),
|
|
798
|
+
attributeArguments: attributeArgumentsOf(node),
|
|
799
|
+
startLine: node.startPosition.row + 1,
|
|
800
|
+
endLine: node.endPosition.row + 1,
|
|
801
|
+
typeRefs,
|
|
802
|
+
memberProvenance,
|
|
803
|
+
});
|
|
804
|
+
};
|
|
805
|
+
const walk = (node, enclosing) => {
|
|
806
|
+
for (let index = 0; index < node.namedChildCount; index += 1) {
|
|
807
|
+
const child = node.namedChild(index);
|
|
808
|
+
if (child === null)
|
|
809
|
+
continue;
|
|
810
|
+
if (child.type === "using_directive") {
|
|
811
|
+
readUsing(child);
|
|
812
|
+
continue;
|
|
813
|
+
}
|
|
814
|
+
if (child.type === "file_scoped_namespace_declaration") {
|
|
815
|
+
// `namespace Shop.Orders;` — no body. Everything after it in the file is
|
|
816
|
+
// inside it, so the remaining siblings are walked with it in force. This
|
|
817
|
+
// is the modern default and permits exactly one namespace per file.
|
|
818
|
+
const name = child.childForFieldName("name")?.text.trim();
|
|
819
|
+
const nested = name === undefined ? enclosing : enclosing === "" ? name : `${enclosing}.${name}`;
|
|
820
|
+
if (namespaceName === "" && nested !== "")
|
|
821
|
+
namespaceName = nested;
|
|
822
|
+
for (let after = index + 1; after < node.namedChildCount; after += 1) {
|
|
823
|
+
const sibling = node.namedChild(after);
|
|
824
|
+
if (sibling === null)
|
|
825
|
+
continue;
|
|
826
|
+
const form = FORMS[sibling.type];
|
|
827
|
+
if (form !== undefined)
|
|
828
|
+
readType(sibling, form, []);
|
|
829
|
+
else if (sibling.type === "using_directive")
|
|
830
|
+
readUsing(sibling);
|
|
831
|
+
}
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
if (child.type === "namespace_declaration") {
|
|
835
|
+
// Block form. A file may legally declare several, and this reader takes
|
|
836
|
+
// the *first* — recorded as a known simplification rather than left as a
|
|
837
|
+
// silent one. Types after a second `namespace` block would be given a
|
|
838
|
+
// namespace they do not have.
|
|
839
|
+
const name = child.childForFieldName("name")?.text.trim();
|
|
840
|
+
const nested = name === undefined ? enclosing : enclosing === "" ? name : `${enclosing}.${name}`;
|
|
841
|
+
if (namespaceName === "" && nested !== "")
|
|
842
|
+
namespaceName = nested;
|
|
843
|
+
const body = child.childForFieldName("body");
|
|
844
|
+
if (body !== null)
|
|
845
|
+
walk(body, nested);
|
|
846
|
+
continue;
|
|
847
|
+
}
|
|
848
|
+
const form = FORMS[child.type];
|
|
849
|
+
if (form !== undefined) {
|
|
850
|
+
readType(child, form, []);
|
|
851
|
+
continue;
|
|
852
|
+
}
|
|
853
|
+
if (child.type === "preproc_if" || child.type === "declaration_list" || child.type === "global_statement") {
|
|
854
|
+
walk(child, enclosing);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
};
|
|
858
|
+
walk(root, "");
|
|
859
|
+
return { file, namespace: namespaceName, usings, types, funcs, hasError, assembly: "" };
|
|
860
|
+
}
|
|
861
|
+
//# sourceMappingURL=parse.js.map
|