ucn 5.5.1 → 5.5.2

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.
Files changed (93) hide show
  1. package/.claude/skills/ucn/SKILL.md +35 -9
  2. package/.claude/skills/ucn/references/commands.md +38 -16
  3. package/.claude/skills/ucn/references/resolution-rules.md +126 -20
  4. package/.claude/skills/ucn/references/trust-contract.md +5 -3
  5. package/README.md +30 -21
  6. package/cli/index.js +1 -1
  7. package/core/accessors.js +341 -54
  8. package/core/account.js +74 -20
  9. package/core/analysis.js +257 -125
  10. package/core/ast-analysis.js +12 -3
  11. package/core/brief.js +8 -4
  12. package/core/build-worker.js +2 -1
  13. package/core/cache.js +137 -6
  14. package/core/call-name-cache.js +134 -0
  15. package/core/callable-scope.js +21 -0
  16. package/core/callers.js +4726 -940
  17. package/core/check.js +22 -5
  18. package/core/class-identity.js +399 -12
  19. package/core/constructor-calls.js +831 -0
  20. package/core/constructor-hierarchy.js +147 -0
  21. package/core/constructor-protocol.js +196 -0
  22. package/core/contract-membership.js +346 -140
  23. package/core/cpp-conversion-constraints.js +123 -0
  24. package/core/cpp-conversion-syntax.js +216 -0
  25. package/core/cpp-conversions.js +414 -0
  26. package/core/cpp-scope.js +245 -44
  27. package/core/deadcode.js +35 -15
  28. package/core/decorator-binding.js +137 -3
  29. package/core/enum-reference-syntax.js +134 -0
  30. package/core/execute.js +17 -1
  31. package/core/external-macros.js +42 -10
  32. package/core/go-packages.js +171 -0
  33. package/core/graph-build.js +2 -0
  34. package/core/graph.js +75 -59
  35. package/core/ignore-rules.js +2 -1
  36. package/core/imports.js +145 -19
  37. package/core/index-ir.js +41 -4
  38. package/core/ir.js +27 -6
  39. package/core/macro-expansion.js +38 -2
  40. package/core/out-of-class-members.js +182 -0
  41. package/core/output/analysis-ext.js +4 -2
  42. package/core/output/analysis.js +89 -20
  43. package/core/output/graph.js +12 -3
  44. package/core/output/lines.js +4 -3
  45. package/core/output/public.js +1 -0
  46. package/core/output/reporting.js +6 -1
  47. package/core/output/shared.js +2 -0
  48. package/core/output/tracing.js +28 -22
  49. package/core/output-budget.js +2 -2
  50. package/core/parallel-build.js +52 -1
  51. package/core/project.js +217 -112
  52. package/core/provenance-constructor-base.js +161 -0
  53. package/core/provenance-facts.js +257 -27
  54. package/core/provenance-overload.js +458 -54
  55. package/core/provenance-type.js +487 -0
  56. package/core/provenance.js +234 -15
  57. package/core/public-command.js +17 -2
  58. package/core/query-bodies.js +159 -0
  59. package/core/reflection.js +255 -2
  60. package/core/rename-token-syntax.js +111 -0
  61. package/core/reporting.js +13 -7
  62. package/core/rust-macro-expansion.js +151 -19
  63. package/core/rust-modules.js +1 -1
  64. package/core/rust-names.js +607 -0
  65. package/core/search.js +70 -17
  66. package/core/shared.js +98 -1
  67. package/core/source-memo.js +201 -0
  68. package/core/text-usage-syntax.js +38 -0
  69. package/core/tracing.js +7 -1
  70. package/core/type-denotation.js +124 -37
  71. package/core/type-references.js +615 -246
  72. package/core/value-references.js +2777 -0
  73. package/core/verify.js +1097 -281
  74. package/languages/adapter.js +28 -4
  75. package/languages/c-family.js +1118 -164
  76. package/languages/constructor-bases.js +105 -0
  77. package/languages/cpp-platform.js +26 -0
  78. package/languages/cpp-site-syntax.js +83 -0
  79. package/languages/csharp-scope.js +439 -0
  80. package/languages/csharp.js +428 -68
  81. package/languages/go.js +385 -207
  82. package/languages/html.js +4 -2
  83. package/languages/index.js +397 -2
  84. package/languages/java-platform.js +226 -0
  85. package/languages/java-unicode.js +269 -0
  86. package/languages/java.js +461 -117
  87. package/languages/javascript.js +418 -66
  88. package/languages/lexical-scope.js +190 -23
  89. package/languages/python.js +404 -54
  90. package/languages/rust.js +510 -82
  91. package/languages/utils.js +67 -8
  92. package/mcp/server.js +8 -2
  93. package/package.json +2 -2
@@ -0,0 +1,123 @@
1
+ 'use strict';
2
+
3
+ const { parsedSource } = require('./source-memo');
4
+
5
+ const INVALID = Symbol('substitution-failure');
6
+
7
+ /** Three-valued constraint evaluation; INVALID is a failed substitution. */
8
+ function constraintResult(index, syntax, context, bindings, convertible) {
9
+ if (!syntax.templateComplete || syntax.requires.some(clause => !clause.complete)) return null;
10
+ if (syntax.templateAlternatives?.length > 1) {
11
+ const results = syntax.templateAlternatives.map(template => constraintResult(index,
12
+ { ...syntax, template, templateAlternatives: [] }, context, bindings, convertible));
13
+ return results.every(result => result === false) ? false : results.every(result => result === true) ? true : null;
14
+ }
15
+ const { resolveType, sameType } = require('./cpp-conversions');
16
+ const type = text => resolveType(index, text, context, bindings);
17
+ const expression = text => {
18
+ const tree = parsedSource('cpp', `constexpr bool probe = ${text};`);
19
+ if (!tree || tree.rootNode.hasError) return null;
20
+ const declaration = tree.rootNode.namedChildren.find(node => node.type === 'declaration');
21
+ const init = declaration?.namedChildren.find(node => node.type === 'init_declarator');
22
+ return evaluate(init?.childForFieldName('value'));
23
+ };
24
+ const traitValue = resolved => {
25
+ if (resolved.kind === 'invalid') return INVALID;
26
+ if (resolved.kind !== 'external') return null;
27
+ const args = resolved.arguments || [];
28
+ if (['std::is_same::value', 'std::is_same_v'].includes(resolved.qualified) && args.length === 2) {
29
+ const a = type(args[0]), b = type(args[1]);
30
+ return a.kind === 'invalid' || b.kind === 'invalid' ? INVALID : sameType(a, b);
31
+ }
32
+ if (['std::is_convertible::value', 'std::is_convertible_v'].includes(resolved.qualified) && args.length === 2) {
33
+ const a = type(args[0]), b = type(args[1]);
34
+ return a.kind === 'invalid' || b.kind === 'invalid' ? INVALID : convertible(a, b);
35
+ }
36
+ return null;
37
+ };
38
+ const validType = node => {
39
+ if (!node) return null;
40
+ const typeSyntax = ['primitive_type', 'type_identifier', 'qualified_identifier', 'template_type',
41
+ 'type_descriptor', 'dependent_type', 'sized_type_specifier'].includes(node.type);
42
+ const resolved = typeSyntax ? type(node.text) : { kind: 'unknown' };
43
+ if (resolved.kind === 'invalid') return INVALID;
44
+ for (const child of node.namedChildren) {
45
+ if (child.type === 'requires_expression') continue;
46
+ if (validType(child) === INVALID) return INVALID;
47
+ }
48
+ if (resolved.kind === 'external' && ['std::enable_if::type', 'std::enable_if_t'].includes(resolved.qualified)) {
49
+ const condition = expression(resolved.arguments[0]);
50
+ return condition === false || condition === INVALID ? INVALID : condition;
51
+ }
52
+ return ['builtin', 'project', 'enum', 'standard'].includes(resolved.kind) ? true : null;
53
+ };
54
+ const evaluate = node => {
55
+ if (!node) return null;
56
+ if (node.type === 'true') return true;
57
+ if (node.type === 'false') return false;
58
+ if (node.type === 'number_literal' && /^[01]$/.test(node.text)) return node.text === '1';
59
+ if (node.type === 'parenthesized_expression') return evaluate(node.namedChildren[0]);
60
+ if (node.type === 'requires_expression') {
61
+ const requirements = node.childForFieldName('requirements');
62
+ for (const requirement of requirements?.namedChildren || []) {
63
+ if (validType(requirement) === INVALID) return false;
64
+ }
65
+ return null;
66
+ }
67
+ if (['binary_expression', 'constraint_conjunction', 'constraint_disjunction'].includes(node.type)) {
68
+ const a = evaluate(node.childForFieldName('left') || node.namedChildren[0]);
69
+ const b = evaluate(node.childForFieldName('right') || node.namedChildren.at(-1));
70
+ const operator = node.childForFieldName('operator')?.text ||
71
+ (node.type === 'constraint_conjunction' ? '&&' : node.type === 'constraint_disjunction' ? '||' : null);
72
+ if (operator === '&&' || operator === 'and') {
73
+ if (a === false || b === false) return false;
74
+ if (a === INVALID || b === INVALID) return INVALID;
75
+ return a === true && b === true ? true : null;
76
+ }
77
+ if (operator === '||' || operator === 'or') {
78
+ // A nested requires-expression can fail without invalidating
79
+ // its enclosing disjunction. An undecidable substitution
80
+ // outside it never supplies a negative proof here.
81
+ if ((a === true || b === true) && a !== INVALID && b !== INVALID) return true;
82
+ return a === false && b === false ? false : null;
83
+ }
84
+ if (a === INVALID || b === INVALID) return INVALID;
85
+ if (typeof a === 'boolean' && typeof b === 'boolean' && ['==', '!='].includes(operator)) {
86
+ return operator === '==' ? a === b : a !== b;
87
+ }
88
+ return null;
89
+ }
90
+ if (node.type === 'unary_expression') {
91
+ const value = evaluate(node.childForFieldName('argument'));
92
+ if (value === INVALID) return INVALID;
93
+ return node.childForFieldName('operator')?.text === '!' && typeof value === 'boolean' ? !value : null;
94
+ }
95
+ const trait = traitValue(type(node.text));
96
+ if (trait !== null) return trait;
97
+ return validType(node) === INVALID ? INVALID : null;
98
+ };
99
+ let uncertain = false;
100
+ if (syntax.template) {
101
+ const tree = parsedSource('cpp', `template ${syntax.template.text} struct Probe {};`);
102
+ const template = tree?.rootNode.namedChildren.find(node => node.type === 'template_declaration');
103
+ for (const parameter of template?.childForFieldName('parameters')?.namedChildren || []) {
104
+ const constrainedType = parameter.childForFieldName('default_type') ||
105
+ (parameter.type === 'optional_parameter_declaration' ? parameter.childForFieldName('type') : null);
106
+ if (!constrainedType) continue;
107
+ const valid = validType(constrainedType);
108
+ if (valid === INVALID) return false;
109
+ if (valid !== true) uncertain = true;
110
+ }
111
+ }
112
+ for (const clause of syntax.requires) {
113
+ const tree = parsedSource('cpp', `template<class Probe> ${clause.text} void probe();`);
114
+ if (!tree || tree.rootNode.hasError) return null;
115
+ const requires = tree.rootNode.descendantsOfType('requires_clause')[0];
116
+ const result = evaluate(requires?.childForFieldName('constraint') || requires?.namedChildren[0]);
117
+ if (result === false || result === INVALID) return false;
118
+ if (result !== true) uncertain = true;
119
+ }
120
+ return uncertain ? null : true;
121
+ }
122
+
123
+ module.exports = { constraintResult };
@@ -0,0 +1,216 @@
1
+ 'use strict';
2
+
3
+ const { getParser, safeParse } = require('../languages');
4
+ const { SourceMemo, parsedSource } = require('./source-memo');
5
+ const { projectFact } = require('./provenance-overload');
6
+ const { ownConstructors } = require('./constructor-calls');
7
+ const { expandDeclaration } = require('./macro-expansion');
8
+ const { ownerDefinitionOf } = require('./class-identity');
9
+ const { declarationIdentity, sameDeclaration } = require('./provenance');
10
+
11
+ const snippets = new SourceMemo();
12
+ const CLASSES = new Set(['class_specifier', 'struct_specifier', 'union_specifier', 'enum_specifier']);
13
+ const DECLARATIONS = new Set([...CLASSES, 'alias_declaration', 'type_definition', 'function_definition',
14
+ 'field_declaration', 'declaration', 'template_declaration']);
15
+
16
+ function definitionNode(tree, definition) {
17
+ const start = definition.sourceSpan?.start;
18
+ if (!Number.isInteger(start)) return null;
19
+ let node = tree.rootNode.descendantForIndex(start, start + 1);
20
+ while (node && !DECLARATIONS.has(node.type)) node = node.parent;
21
+ if (node?.type === 'template_declaration') return node.namedChildren.find(child =>
22
+ DECLARATIONS.has(child.type) && child.type !== 'template_declaration') || node;
23
+ return node;
24
+ }
25
+
26
+ function headerNodes(node, result = []) {
27
+ if (!node || node.type === 'compound_statement') return result;
28
+ result.push(node);
29
+ for (const child of node.namedChildren) headerNodes(child, result);
30
+ return result;
31
+ }
32
+
33
+ function inPreprocessorCondition(node) {
34
+ for (let parent = node.parent; parent; parent = parent.parent) {
35
+ if (!parent.type.startsWith('preproc_')) continue;
36
+ const condition = parent.childForFieldName('condition') || parent.childForFieldName('name');
37
+ if (condition && condition.startIndex <= node.startIndex && node.endIndex <= condition.endIndex) return true;
38
+ }
39
+ return false;
40
+ }
41
+
42
+ function templateParameters(node) {
43
+ if (!node) return [];
44
+ return node.namedChildren.filter(child => !child.type.endsWith('comment')).map(child => {
45
+ const name = child.childForFieldName('name') || child.childForFieldName('declarator') ||
46
+ child.namedChildren.find(item => ['type_identifier', 'identifier'].includes(item.type));
47
+ const value = child.childForFieldName('default_type') || child.childForFieldName('default_value');
48
+ return { name: name?.text || null, default: value?.text || null,
49
+ variadic: child.type.startsWith('variadic_'), type: child.type };
50
+ });
51
+ }
52
+
53
+ function templateSyntax(text) {
54
+ let result = snippets.get('cpp-template-parameters', text);
55
+ if (result) return result;
56
+ const source = `template ${text} struct Probe {};`;
57
+ const tree = parsedSource('cpp', source);
58
+ const template = tree?.rootNode.namedChildren.find(node => node.type === 'template_declaration');
59
+ const parameters = template?.childForFieldName('parameters');
60
+ const errors = tree?.rootNode.descendantsOfType('ERROR') || [];
61
+ const trailingComma = parameters?.namedChildren.at(-1);
62
+ result = { complete: !!parameters && !tree.rootNode.hasError,
63
+ impossible: errors.length === 1 && trailingComma?.type === 'ERROR' && trailingComma.text.trim() === ',',
64
+ parameters: templateParameters(parameters), text };
65
+ return snippets.set('cpp-template-parameters', text, result);
66
+ }
67
+
68
+ function sourceView(index, definition) {
69
+ const source = index._readFile(definition.file);
70
+ const entry = index.files.get(definition.file);
71
+ const rawTree = parsedSource('cpp', source);
72
+ const rawNode = definitionNode(rawTree, definition);
73
+ const rawExtent = rawNode?.parent?.type === 'template_declaration' ? rawNode.parent : rawNode;
74
+ const matches = candidate => candidate && candidate.startPosition.row + 1 === definition.startLine &&
75
+ candidate.endPosition.row + 1 <= definition.endLine;
76
+ if (!['class', 'struct', 'union', 'enum'].includes(definition.type) && rawNode && !CLASSES.has(rawNode.type) && matches(rawExtent)) {
77
+ return { source, tree: rawTree, blanks: entry?.recoveryBlanks || [], node: rawNode };
78
+ }
79
+ const tree = require('../languages/cpp').recoveredTree(source, getParser('cpp'), entry?.recoveryBlanks) ||
80
+ safeParse(getParser('cpp'), source);
81
+ const node = definitionNode(tree, definition);
82
+ const extent = node?.parent?.type === 'template_declaration' ? node.parent : node;
83
+ return { source, tree, blanks: entry?.recoveryBlanks || [],
84
+ rawNode: CLASSES.has(rawNode?.type) ? rawNode : null,
85
+ node: ['class', 'struct', 'union', 'enum'].includes(definition.type) || matches(extent) ? node : null };
86
+ }
87
+
88
+ function declarationSyntax(index, definition) {
89
+ const key = `cpp-conversion-declaration:${definition.file}:${definition.sourceSpan?.start}:${definition.name}`;
90
+ return projectFact(index, key, live => {
91
+ const memo = require('./cpp-scope').conversionSyntaxMemo(live);
92
+ if (memo?.has(key)) return memo.get(key);
93
+ const { source, node } = sourceView(live, definition);
94
+ const template = node?.parent?.type === 'template_declaration' ? node.parent : null;
95
+ const parameters = template?.childForFieldName('parameters');
96
+ const expanded = parameters ? expandDeclaration(live, definition.file, parameters.startPosition.row + 1,
97
+ source.slice(parameters.startIndex, parameters.endIndex)) : null;
98
+ const variants = expanded?.variants.map(variant => ({ ...templateSyntax(variant.text),
99
+ expansionComplete: variant.complete })) || [];
100
+ const valid = variants.filter(variant => variant.complete);
101
+ const syntax = valid[0] || variants[0] || null;
102
+ const headers = CLASSES.has(node?.type) ? [] : headerNodes(template || node);
103
+ const requires = headers.filter(child => child.type === 'requires_clause')
104
+ .map(child => expandDeclaration(live, definition.file, child.startPosition.row + 1,
105
+ source.slice(child.startIndex, child.endIndex)));
106
+ const explicit = headers.find(child => child.type === 'explicit_function_specifier');
107
+ const conversion = headers.find(child => child.type === 'operator_cast');
108
+ const conversionKeyword = conversion?.children.find(child => child.type === 'operator');
109
+ const conversionParameters = conversion && headerNodes(conversion).find(child =>
110
+ child.type === 'abstract_function_declarator')?.childForFieldName('parameters');
111
+ const parameterList = headers.find(child => child.type === 'function_declarator')?.childForFieldName('parameters');
112
+ const parameterTypes = (parameterList?.namedChildren || []).map(parameter => {
113
+ const declarator = parameter.childForFieldName('declarator');
114
+ if (declarator && !declarator.type.startsWith('abstract_')) return null;
115
+ const equals = parameter.children.find(child => child.type === '=');
116
+ return source.slice(parameter.startIndex, equals?.startIndex ?? parameter.endIndex).trim();
117
+ });
118
+ const result = { template: syntax, templateAlternatives: valid,
119
+ templateComplete: !!node && (!expanded || valid.length > 0 && variants.every(variant =>
120
+ variant.expansionComplete && (variant.complete || variant.impossible))),
121
+ requires, explicit: explicit?.text || null,
122
+ isConversion: !!conversion,
123
+ // The type field excludes pointer/reference declarators and cv
124
+ // qualifiers. The conversion-type-id ends at the function's
125
+ // parameter list, before its own cv/ref qualifiers.
126
+ conversionType: conversionKeyword && conversionParameters
127
+ ? source.slice(conversionKeyword.endIndex, conversionParameters.startIndex).trim() : null,
128
+ parameterTypes,
129
+ nodeType: node?.type || null, complete: !!node && !node.hasError,
130
+ ...(parameters && { templateRange: [parameters.startIndex, parameters.endIndex] }) };
131
+ memo?.set(key, result);
132
+ return result;
133
+ });
134
+ }
135
+
136
+ function classSyntax(index, owner) {
137
+ const key = `cpp-conversion-class:${owner.file}:${owner.sourceSpan?.start}:${owner.name}`;
138
+ return projectFact(index, key, live => {
139
+ const memo = require('./cpp-scope').conversionSyntaxMemo(live);
140
+ if (memo?.has(key)) return memo.get(key);
141
+ const { source, node, blanks, rawNode } = sourceView(live, owner);
142
+ const body = node?.childForFieldName('body');
143
+ if (!body || !CLASSES.has(node.type)) return { complete: false, constructors: [], operators: [], bases: [], members: [] };
144
+ const constructors = ownConstructors(live, owner).map(definition => ({
145
+ definition, syntax: declarationSyntax(live, definition),
146
+ }));
147
+ const nodes = headerNodes(body);
148
+ const functions = nodes.filter(child => child.type === 'function_definition');
149
+ const bodies = functions.map(child => child.childForFieldName('body')).filter(Boolean);
150
+ const contains = (range, start, end) => range[0] <= start && end <= range[1];
151
+ const repairedTemplates = constructors.filter(ctor => ctor.syntax.templateComplete)
152
+ .map(ctor => ctor.syntax.templateRange).filter(Boolean);
153
+ const inBody = (start, end) => bodies.some(child => child.startIndex <= start && end <= child.endIndex);
154
+ let complete = !nodes.some(child => (child.type === 'ERROR' || child.isMissing) &&
155
+ !repairedTemplates.some(range => contains(range, child.startIndex, child.endIndex)));
156
+ // A using-declaration can inherit constructors. A recovered member
157
+ // may hide another constructor, unless its constraint was rebuilt
158
+ // from the actual macro expansion or the change is inside a body.
159
+ if (nodes.some(child => child.type === 'using_declaration')) complete = false;
160
+ for (const [start, end] of blanks) {
161
+ if (end <= body.startIndex || start >= body.endIndex || inBody(start, end) ||
162
+ repairedTemplates.some(range => contains(range, start, end))) continue;
163
+ const fragment = require('../languages/c-family').conditionalHeaderText(source.slice(start, end));
164
+ const expanded = expandDeclaration(live, owner.file,
165
+ source.slice(0, start).split('\n').length, fragment);
166
+ if (!expanded.variants.every(variant => variant.complete) || require('../languages/c-family')
167
+ .objectMacroReading(getParser('cpp'), expanded.variants.map(variant => variant.text)) !== 'decoration') complete = false;
168
+ }
169
+ const rawBody = rawNode?.childForFieldName('body');
170
+ const rawHeaders = rawBody ? headerNodes(rawBody) : [];
171
+ // A conditional parse view may omit an otherwise complete member
172
+ // branch. Its literal AST still supplies that inventory when every
173
+ // damaged header was reconstructed from its macro definitions.
174
+ if (!complete && rawBody && !rawHeaders.some(child => child.type === 'using_declaration' ||
175
+ (child.type === 'ERROR' || child.isMissing) &&
176
+ !repairedTemplates.some(range => contains(range, child.startIndex, child.endIndex)))) {
177
+ const declarations = rawHeaders.filter(child => ['declaration', 'field_declaration', 'function_definition'].includes(child.type));
178
+ const unknownMacro = declarations.some(declaration => {
179
+ const function_ = declaration.childForFieldName('declarator');
180
+ const name = function_?.type === 'function_declarator' && function_.childForFieldName('declarator');
181
+ return name?.type === 'identifier' && (live.symbols.get(name.text) || []).some(def => def.type === 'macro');
182
+ });
183
+ const constructorHeaders = rawHeaders.filter(child => child.type === 'function_declarator' &&
184
+ child.childForFieldName('declarator')?.text === owner.name);
185
+ const allRecorded = constructorHeaders.every(header => constructors.some(ctor =>
186
+ ctor.definition.startLine <= header.startPosition.row + 1 &&
187
+ header.endPosition.row + 1 <= ctor.definition.endLine));
188
+ const macroTokens = rawHeaders.filter(child => ['identifier', 'type_identifier'].includes(child.type) &&
189
+ !inPreprocessorCondition(child) &&
190
+ !repairedTemplates.some(range => contains(range, child.startIndex, child.endIndex)) &&
191
+ (live.symbols.get(child.text) || []).some(def => def.type === 'macro'));
192
+ const macrosDecorative = macroTokens.every(token => {
193
+ const expanded = expandDeclaration(live, owner.file, token.startPosition.row + 1, token.text);
194
+ return expanded.variants.every(variant => variant.complete) && require('../languages/c-family')
195
+ .objectMacroReading(getParser('cpp'), expanded.variants.map(variant => variant.text)) === 'decoration';
196
+ });
197
+ if (!unknownMacro && allRecorded && macrosDecorative) complete = true;
198
+ }
199
+ const identity = declarationIdentity(owner);
200
+ const members = (live.files.get(owner.file)?.symbols || []).filter(definition =>
201
+ sameDeclaration(declarationIdentity(ownerDefinitionOf(live, definition)), identity) ||
202
+ definition.enclosingType === owner.name && definition.lexicalScopeStartLine === owner.startLine &&
203
+ definition.lexicalScopeEndLine === owner.endLine);
204
+ const operators = members.filter(definition => definition.name.startsWith('operator '))
205
+ .map(definition => ({ definition, syntax: declarationSyntax(live, definition) }))
206
+ .filter(operator => operator.syntax.isConversion);
207
+ const result = { complete, constructors, operators,
208
+ template: declarationSyntax(live, owner).template,
209
+ bases: require('./graph-build').splitParentList(owner.extends || ''),
210
+ members: members.filter(definition => ['type', 'class', 'struct', 'enum', 'union'].includes(definition.type)) };
211
+ memo?.set(key, result);
212
+ return result;
213
+ });
214
+ }
215
+
216
+ module.exports = { declarationSyntax, classSyntax, templateSyntax, definitionNode };