ucn 5.5.0 → 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.
- package/.claude/skills/ucn/SKILL.md +35 -9
- package/.claude/skills/ucn/references/commands.md +38 -16
- package/.claude/skills/ucn/references/resolution-rules.md +126 -20
- package/.claude/skills/ucn/references/trust-contract.md +5 -3
- package/README.md +41 -27
- package/cli/index.js +6 -1
- package/core/accessors.js +341 -54
- package/core/account.js +106 -31
- package/core/analysis.js +266 -127
- package/core/ast-analysis.js +12 -3
- package/core/brief.js +8 -4
- package/core/build-worker.js +2 -1
- package/core/cache.js +144 -6
- package/core/call-name-cache.js +134 -0
- package/core/callable-scope.js +21 -0
- package/core/callers.js +5111 -1024
- package/core/check.js +22 -5
- package/core/class-identity.js +399 -12
- package/core/constructor-calls.js +831 -0
- package/core/constructor-hierarchy.js +147 -0
- package/core/constructor-protocol.js +196 -0
- package/core/contract-membership.js +346 -140
- package/core/cpp-conversion-constraints.js +123 -0
- package/core/cpp-conversion-syntax.js +216 -0
- package/core/cpp-conversions.js +414 -0
- package/core/cpp-scope.js +245 -44
- package/core/deadcode.js +35 -15
- package/core/decorator-binding.js +137 -3
- package/core/enum-reference-syntax.js +134 -0
- package/core/execute.js +17 -1
- package/core/external-macros.js +42 -10
- package/core/go-packages.js +171 -0
- package/core/graph-build.js +2 -0
- package/core/graph.js +75 -59
- package/core/ignore-rules.js +2 -1
- package/core/imports.js +145 -19
- package/core/index-ir.js +41 -4
- package/core/ir.js +27 -6
- package/core/macro-expansion.js +38 -2
- package/core/out-of-class-members.js +182 -0
- package/core/output/analysis-ext.js +4 -2
- package/core/output/analysis.js +89 -20
- package/core/output/graph.js +12 -3
- package/core/output/lines.js +4 -3
- package/core/output/public.js +1 -0
- package/core/output/reporting.js +6 -1
- package/core/output/shared.js +2 -0
- package/core/output/tracing.js +28 -22
- package/core/output-budget.js +2 -2
- package/core/parallel-build.js +52 -1
- package/core/project.js +228 -114
- package/core/provenance-constructor-base.js +161 -0
- package/core/provenance-facts.js +257 -27
- package/core/provenance-overload.js +458 -54
- package/core/provenance-type.js +487 -0
- package/core/provenance.js +234 -15
- package/core/public-command.js +17 -2
- package/core/query-bodies.js +159 -0
- package/core/reflection.js +255 -2
- package/core/rename-token-syntax.js +111 -0
- package/core/reporting.js +13 -7
- package/core/rust-macro-expansion.js +1202 -157
- package/core/rust-modules.js +21 -3
- package/core/rust-names.js +607 -0
- package/core/search.js +70 -17
- package/core/shared.js +98 -1
- package/core/source-memo.js +201 -0
- package/core/text-usage-syntax.js +38 -0
- package/core/tracing.js +7 -1
- package/core/type-denotation.js +157 -46
- package/core/type-references.js +615 -246
- package/core/value-references.js +2777 -0
- package/core/verify.js +1097 -281
- package/languages/adapter.js +28 -4
- package/languages/c-family.js +1118 -164
- package/languages/constructor-bases.js +105 -0
- package/languages/cpp-platform.js +26 -0
- package/languages/cpp-site-syntax.js +83 -0
- package/languages/csharp-scope.js +439 -0
- package/languages/csharp.js +428 -68
- package/languages/go.js +385 -207
- package/languages/html.js +4 -2
- package/languages/index.js +397 -2
- package/languages/java-platform.js +226 -0
- package/languages/java-unicode.js +269 -0
- package/languages/java.js +461 -117
- package/languages/javascript.js +418 -66
- package/languages/lexical-scope.js +190 -23
- package/languages/python.js +404 -54
- package/languages/rust.js +548 -89
- package/languages/utils.js +67 -8
- package/mcp/server.js +8 -2
- package/package.json +2 -2
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { typeOrigin } = require('./type-evidence');
|
|
4
|
+
const { sameNode, genericArityOf, nodeTextWithoutComments } = require('./utils');
|
|
5
|
+
|
|
6
|
+
/** A type's last AST name and its written qualifier, retaining global roots. */
|
|
7
|
+
function baseSpelling(node, separator) {
|
|
8
|
+
if (!node) return null;
|
|
9
|
+
if (['primary_constructor_base_type', 'generic_type', 'template_type', 'generic_name'].includes(node.type)) {
|
|
10
|
+
return baseSpelling(node.childForFieldName('name') || node.childForFieldName('type') || node.namedChild(0), separator);
|
|
11
|
+
}
|
|
12
|
+
if (['identifier', 'type_identifier', 'field_identifier'].includes(node.type)) return { name: node.text, qualifier: null };
|
|
13
|
+
if (['qualified_identifier', 'qualified_name', 'scoped_type_identifier', 'alias_qualified_name'].includes(node.type)) {
|
|
14
|
+
const last = node.childForFieldName('name') || node.namedChildren.at(-1);
|
|
15
|
+
const tail = baseSpelling(last, separator);
|
|
16
|
+
if (!tail) return null;
|
|
17
|
+
let prefix = node.text.slice(0, last.startIndex - node.startIndex).trim();
|
|
18
|
+
if (prefix.endsWith(separator) && prefix !== separator) prefix = prefix.slice(0, -separator.length).trim();
|
|
19
|
+
return { name: tail.name, qualifier: prefix || tail.qualifier };
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const spellingKey = spelling => spelling && `${spelling.qualifier || ''}\0${spelling.name}`;
|
|
25
|
+
|
|
26
|
+
/** Calls the language supplies when a constructor omits its base initializer. */
|
|
27
|
+
function implicitBaseCalls(root, language, reaches, owners = null) {
|
|
28
|
+
const config = require('./index').langTraits(language)?.implicitBaseConstruction;
|
|
29
|
+
if (!config) return [];
|
|
30
|
+
const calls = [];
|
|
31
|
+
for (const owner of owners || root.descendantsOfType(config.classes)) {
|
|
32
|
+
if (owner.hasError || reaches && !reaches(owner) ||
|
|
33
|
+
owner.children.some(child => child.type === 'struct') && config.referenceTypesOnly) continue;
|
|
34
|
+
const nameNode = owner.childForFieldName('name');
|
|
35
|
+
const ownerName = baseSpelling(nameNode, config.separator)?.name;
|
|
36
|
+
if (!ownerName) continue;
|
|
37
|
+
const header = config.headerField ? owner.childForFieldName(config.headerField)
|
|
38
|
+
: owner.namedChildren.find(child => child.type === config.headerNode);
|
|
39
|
+
if (!header) continue;
|
|
40
|
+
let bases = header.namedChildren.filter(child => baseSpelling(child, config.separator));
|
|
41
|
+
if (config.firstBaseOnly) bases = bases.slice(0, 1);
|
|
42
|
+
if (!bases.length || header.namedChildren.some(child => child.type === 'argument_list') ||
|
|
43
|
+
bases.some(base => base.namedChildren.some(child => child.type === 'argument_list'))) continue;
|
|
44
|
+
const scope = config.ownerScope?.(owner) || {};
|
|
45
|
+
const body = owner.childForFieldName('body');
|
|
46
|
+
const constructors = (body?.namedChildren || []).filter(node => config.constructors.includes(node.type))
|
|
47
|
+
.map(node => {
|
|
48
|
+
if (config.constructorInfo) return config.constructorInfo(node, ownerName, owner);
|
|
49
|
+
const name = node.childForFieldName('name');
|
|
50
|
+
if (name?.text !== ownerName || node.namedChildren.some(child => child.type === 'modifier' && child.text === 'static')) return null;
|
|
51
|
+
const delegated = node.descendantsOfType(config.delegationNodes).some(call => {
|
|
52
|
+
for (let parent = call.parent; parent; parent = parent.parent) {
|
|
53
|
+
if (sameNode(parent, node)) return true;
|
|
54
|
+
if (config.classes.includes(parent.type) || config.constructors.includes(parent.type)) return false;
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
});
|
|
58
|
+
return { node, name, body: node.childForFieldName('body'), suppressed: delegated };
|
|
59
|
+
}).filter(Boolean);
|
|
60
|
+
const sources = constructors.length ? constructors : [{ node: owner, name: nameNode, body,
|
|
61
|
+
potential: !!config.defaultPotential, generated: true }];
|
|
62
|
+
for (const source of sources) {
|
|
63
|
+
if (source.suppressed || !source.body || reaches && !reaches(source.node)) continue;
|
|
64
|
+
const initialized = source.initialized || [];
|
|
65
|
+
for (const base of bases) {
|
|
66
|
+
const spelling = baseSpelling(base, config.separator);
|
|
67
|
+
if (initialized.some(entry => !entry.field && spellingKey(entry) === spellingKey(spelling))) continue;
|
|
68
|
+
const ambiguous = initialized.some(entry => !entry.field &&
|
|
69
|
+
!bases.some(other => spellingKey(entry) === spellingKey(baseSpelling(other, config.separator))));
|
|
70
|
+
const site = source.name;
|
|
71
|
+
const arity = genericArityOf(base.text);
|
|
72
|
+
const parameters = source.node.childForFieldName('parameters');
|
|
73
|
+
const params = source.params ?? (parameters
|
|
74
|
+
? nodeTextWithoutComments(parameters).replace(/^\(|\)$/g, '').trim() : null);
|
|
75
|
+
calls.push({ callSite: typeOrigin('call', site), name: spelling.name,
|
|
76
|
+
line: site.startPosition.row + 1, isMethod: false, isConstructor: true,
|
|
77
|
+
constructorSyntax: 'implicit', argCount: 0,
|
|
78
|
+
...(spelling.qualifier && { constructorQualifier: spelling.qualifier }),
|
|
79
|
+
...(arity > 0 && { typeArgs: arity }),
|
|
80
|
+
implicitBase: { owner: ownerName, ownerLine: owner.startPosition.row + 1,
|
|
81
|
+
ownerEndLine: owner.endPosition.row + 1, scope,
|
|
82
|
+
generated: !!source.generated, params,
|
|
83
|
+
header: typeOrigin('type', base), arity, potential: !!(source.potential || ambiguous) },
|
|
84
|
+
enclosingFunction: { name: ownerName, startLine: source.node.startPosition.row + 1,
|
|
85
|
+
endLine: source.node.endPosition.row + 1 } });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return calls;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* The omitted-initializer calls of the classes spanning exactly
|
|
94
|
+
* `ownerLine`..`ownerEndLine` (fix #421): what implicitBaseCalls yields for
|
|
95
|
+
* them in a whole-tree walk, without extracting every other call.
|
|
96
|
+
*/
|
|
97
|
+
function implicitBaseCallsOfOwner(root, language, reaches, ownerLine, ownerEndLine) {
|
|
98
|
+
const config = require('./index').langTraits(language)?.implicitBaseConstruction;
|
|
99
|
+
if (!config || !root) return [];
|
|
100
|
+
const owners = root.descendantsOfType(config.classes).filter(node =>
|
|
101
|
+
node.startPosition.row + 1 === ownerLine && node.endPosition.row + 1 === ownerEndLine);
|
|
102
|
+
return owners.length ? implicitBaseCalls(root, language, reaches, owners) : [];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
module.exports = { implicitBaseCalls, implicitBaseCallsOfOwner, baseSpelling };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Qualified standard-library contracts. Name lookup belongs to cpp-scope;
|
|
4
|
+
// none of these facts apply to a project declaration with the same name.
|
|
5
|
+
const STRING_ALIASES = new Map([
|
|
6
|
+
['string', 'char'], ['wstring', 'wchar_t'], ['u8string', 'char8_t'],
|
|
7
|
+
['u16string', 'char16_t'], ['u32string', 'char32_t'],
|
|
8
|
+
]);
|
|
9
|
+
const CHARACTERS = new Set(['char', 'wchar_t', 'char8_t', 'char16_t', 'char32_t']);
|
|
10
|
+
|
|
11
|
+
function standardStringType(qualified, arguments_ = []) {
|
|
12
|
+
if (!qualified?.startsWith('std::')) return null;
|
|
13
|
+
const name = qualified.slice(5);
|
|
14
|
+
const view = name.endsWith('_view');
|
|
15
|
+
const alias = view ? name.slice(0, -5) : name;
|
|
16
|
+
if (STRING_ALIASES.has(alias) && arguments_.length === 0) {
|
|
17
|
+
return { family: view ? 'view' : 'string', character: STRING_ALIASES.get(alias) };
|
|
18
|
+
}
|
|
19
|
+
if (!['basic_string', 'basic_string_view'].includes(name) || !CHARACTERS.has(arguments_[0])) return null;
|
|
20
|
+
// A specialization with custom traits or allocator is outside this
|
|
21
|
+
// small platform model, as is a project-provided specialization.
|
|
22
|
+
if (arguments_.length > 1) return null;
|
|
23
|
+
return { family: name === 'basic_string' ? 'string' : 'view', character: arguments_[0] };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports = { standardStringType, CHARACTERS };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const C_TYPE_SPECIFIERS = new Set(['class_specifier', 'struct_specifier', 'union_specifier', 'enum_specifier']);
|
|
4
|
+
const textOf = node => node ? node.text.replace(/\s+/g, '') : '';
|
|
5
|
+
const contains = (outer, inner) => outer.startIndex <= inner.startIndex && outer.endIndex >= inner.endIndex;
|
|
6
|
+
|
|
7
|
+
// Only syntax belongs here. Namespace macros, recovered class owners and
|
|
8
|
+
// include visibility are resolved by the current index at query time.
|
|
9
|
+
function cppSiteSyntax(node) {
|
|
10
|
+
const namespaces = [];
|
|
11
|
+
const classes = [];
|
|
12
|
+
let memberOf = null;
|
|
13
|
+
let recovered = false;
|
|
14
|
+
for (let current = node.parent; current; current = current.parent) {
|
|
15
|
+
if (current.type === 'ERROR') recovered = true;
|
|
16
|
+
if (current.type === 'namespace_definition') {
|
|
17
|
+
const nameNode = current.childForFieldName('name');
|
|
18
|
+
if (nameNode) namespaces.unshift(textOf(nameNode));
|
|
19
|
+
} else if (C_TYPE_SPECIFIERS.has(current.type)) {
|
|
20
|
+
const body = current.childForFieldName('body');
|
|
21
|
+
const nameNode = current.childForFieldName('name');
|
|
22
|
+
if (body && contains(body, node) && nameNode) {
|
|
23
|
+
// Specializations keep their arguments (`Acc<double>`), as
|
|
24
|
+
// the index names them; an out-of-class nested definition
|
|
25
|
+
// (`struct List<K>::Node`) is in its owner's scope too
|
|
26
|
+
// (fix #396).
|
|
27
|
+
const segments = textOf(nameNode).split('::');
|
|
28
|
+
classes.unshift(segments.slice(-2).join('::'));
|
|
29
|
+
}
|
|
30
|
+
} else if (current.type === 'function_definition' && !memberOf && classes.length === 0) {
|
|
31
|
+
let declarator = current.childForFieldName('declarator');
|
|
32
|
+
while (declarator && declarator.type !== 'function_declarator' && declarator.childForFieldName?.('declarator')) {
|
|
33
|
+
declarator = declarator.childForFieldName('declarator');
|
|
34
|
+
}
|
|
35
|
+
const qualified = declarator?.childForFieldName?.('declarator');
|
|
36
|
+
if (qualified?.type === 'qualified_identifier') {
|
|
37
|
+
const scope = qualified.childForFieldName('scope');
|
|
38
|
+
// `List<K>::Node::f`: Node's scope, then List's (fix #396).
|
|
39
|
+
if (scope) memberOf = qualifiedScopeText(qualified) || textOf(scope).split('::').pop();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return { namespaces, classes, memberOf, recovered };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** The class path a qualified declarator's scope names, without its
|
|
47
|
+
* leading namespace-looking segments: `ns::List<K>::Node::f` -> the scope
|
|
48
|
+
* segments `List<K>::Node` (the last two at most). */
|
|
49
|
+
function qualifiedScopeText(qualified) {
|
|
50
|
+
const scopes = [];
|
|
51
|
+
let current = qualified;
|
|
52
|
+
while (current?.type === 'qualified_identifier') {
|
|
53
|
+
const scope = current.childForFieldName('scope');
|
|
54
|
+
if (scope) scopes.push(textOf(scope));
|
|
55
|
+
current = current.childForFieldName('name');
|
|
56
|
+
}
|
|
57
|
+
return scopes.length > 0 ? scopes.slice(-2).join('::') : null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Locate the headers the recovered calls ask about in the raw tree.
|
|
61
|
+
// A null syntax records that the exact node is absent, not a binding verdict.
|
|
62
|
+
function cppBaseSyntax(source, parser, positions, tree = null) {
|
|
63
|
+
if (!positions.length) return [];
|
|
64
|
+
const owned = !tree;
|
|
65
|
+
tree ||= require('./index').safeParse(parser, source);
|
|
66
|
+
if (!tree) return null;
|
|
67
|
+
try {
|
|
68
|
+
const rows = [], seen = new Set();
|
|
69
|
+
for (const at of positions) {
|
|
70
|
+
const key = JSON.stringify([at.start, at.end, at.nodeType, at.line, at.column]);
|
|
71
|
+
if (seen.has(key)) continue;
|
|
72
|
+
seen.add(key);
|
|
73
|
+
let node = tree.rootNode.descendantForIndex(at.start);
|
|
74
|
+
while (node && !(node.startIndex === at.start && node.endIndex === at.end && node.type === at.nodeType)) node = node.parent;
|
|
75
|
+
const matches = node && node.startPosition.row + 1 === at.line && node.startPosition.column === at.column;
|
|
76
|
+
rows.push({ start: at.start, end: at.end, nodeType: at.nodeType, line: at.line, column: at.column,
|
|
77
|
+
syntax: matches ? cppSiteSyntax(node) : null });
|
|
78
|
+
}
|
|
79
|
+
return rows;
|
|
80
|
+
} finally { if (owned) tree.delete?.(); }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
module.exports = { cppSiteSyntax, cppBaseSyntax };
|
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// C# lexical binding facts shared by member reference lookups.
|
|
4
|
+
const { sameNode } = require('./utils');
|
|
5
|
+
|
|
6
|
+
// Declarations whose `name` field binds a local of the member body.
|
|
7
|
+
const CSHARP_LOCAL_DECLARATION_NODES = new Set(['parameter', 'catch_declaration', 'declaration_pattern',
|
|
8
|
+
'declaration_expression', 'variable_declarator', 'local_function_statement', 'from_clause',
|
|
9
|
+
'join_clause', 'query_continuation', 'recursive_pattern', 'var_pattern']);
|
|
10
|
+
|
|
11
|
+
// A switch expression arm scopes its pattern variables; a while
|
|
12
|
+
// condition's variables are scoped to the statement (fix #419).
|
|
13
|
+
const CSHARP_BLOCK_SCOPES = new Set(['block', 'switch_section', 'for_statement', 'using_statement',
|
|
14
|
+
'fixed_statement', 'lambda_expression', 'anonymous_method_expression', 'arrow_expression_clause',
|
|
15
|
+
'switch_expression_arm', 'while_statement']);
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The outermost node of a deconstruction designation holding `node`
|
|
19
|
+
* (fix #419): `var (a, (b, c))` in a declarator, foreach or pattern. The
|
|
20
|
+
* grammar reads `x is var (a, b)` as an invocation of `x is var`, whose
|
|
21
|
+
* arguments are then the designations. Null when `node` is no such name.
|
|
22
|
+
*/
|
|
23
|
+
function deconstructionRoot(node) {
|
|
24
|
+
let current = node, root = null;
|
|
25
|
+
while (current.parent && (current.parent.type === 'tuple_pattern' ||
|
|
26
|
+
current.parent.type === 'parenthesized_variable_designation')) {
|
|
27
|
+
current = current.parent;
|
|
28
|
+
root = current;
|
|
29
|
+
}
|
|
30
|
+
if (root) return root;
|
|
31
|
+
// `x is var (a, (b, c))`: argument / tuple_expression nesting under the
|
|
32
|
+
// misread invocation's argument list.
|
|
33
|
+
current = node;
|
|
34
|
+
while (current.parent?.type === 'argument' && current.type !== 'argument' &&
|
|
35
|
+
(current.type === 'identifier' || current.type === 'tuple_expression')) {
|
|
36
|
+
const argument = current.parent, list = argument.parent;
|
|
37
|
+
if (list?.type === 'tuple_expression') { current = list; continue; }
|
|
38
|
+
const call = list?.type === 'argument_list' ? list.parent : null;
|
|
39
|
+
const callee = call?.type === 'invocation_expression' ? call.childForFieldName('function') : null;
|
|
40
|
+
if (callee?.type === 'is_expression' && callee.namedChildren.at(-1)?.type === 'implicit_type' &&
|
|
41
|
+
sameNode(call.childForFieldName('arguments'), list)) return call;
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Where C# scopes a local named like the reference (spec 7.6.1): 'local' when
|
|
49
|
+
* a parameter, local variable, local function, pattern, lambda, foreach,
|
|
50
|
+
* catch or range variable of that name is in scope at `node`, 'none' when
|
|
51
|
+
* no declaration of the member reaches it, 'unknown' for an unmodelled
|
|
52
|
+
* declaration shape.
|
|
53
|
+
*/
|
|
54
|
+
const localBindingsByTree = new WeakMap();
|
|
55
|
+
function bindingVerdict(facts, node) {
|
|
56
|
+
const contains = scope => scope && (scope.ranges ? scope.ranges.some(contains)
|
|
57
|
+
: scope.startIndex <= node.startIndex && node.endIndex <= scope.endIndex);
|
|
58
|
+
let unknown = false;
|
|
59
|
+
for (const fact of facts) {
|
|
60
|
+
if (sameNode(fact.node, node)) continue;
|
|
61
|
+
if (!fact.scope) unknown = true;
|
|
62
|
+
else if (contains(fact.scope)) return 'local';
|
|
63
|
+
}
|
|
64
|
+
return unknown ? 'unknown' : 'none';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function csharpLocalBinding(memberNode, node, name) {
|
|
68
|
+
let bindings = localBindingsByTree.get(memberNode.tree);
|
|
69
|
+
if (!bindings) {
|
|
70
|
+
bindings = new (require('../core/source-memo').BoundedMap)({ maxEntries: 512 });
|
|
71
|
+
localBindingsByTree.set(memberNode.tree, bindings);
|
|
72
|
+
}
|
|
73
|
+
const cacheKey = `${memberNode.id}\0${name}`;
|
|
74
|
+
if (bindings.has(cacheKey)) return bindingVerdict(bindings.get(cacheKey), node);
|
|
75
|
+
const contains = (scope, target) => scope && (scope.ranges
|
|
76
|
+
? scope.ranges.some(range => contains(range, target))
|
|
77
|
+
: scope.startIndex <= target.startIndex && target.endIndex <= scope.endIndex);
|
|
78
|
+
const nearest = (from, kinds) => {
|
|
79
|
+
for (let current = from.parent; current && !sameNode(current, memberNode); current = current.parent) {
|
|
80
|
+
if (kinds.has(current.type)) return current;
|
|
81
|
+
}
|
|
82
|
+
return memberNode;
|
|
83
|
+
};
|
|
84
|
+
const facts = [];
|
|
85
|
+
const snapshot = scope => !scope ? null : scope.ranges
|
|
86
|
+
? { ranges: scope.ranges.map(snapshot) }
|
|
87
|
+
: { startIndex: scope.startIndex, endIndex: scope.endIndex };
|
|
88
|
+
const queryRange = (from, start, end = null) => {
|
|
89
|
+
const query = nearest(from, new Set(['query_expression']));
|
|
90
|
+
const continuation = query.children?.find(child => child.type === 'into' && child.startIndex > start);
|
|
91
|
+
return { startIndex: start, endIndex: end ?? continuation?.startIndex ?? query.endIndex };
|
|
92
|
+
};
|
|
93
|
+
const scopeOf = (declared) => {
|
|
94
|
+
const parent = declared.parent;
|
|
95
|
+
switch (parent.type) {
|
|
96
|
+
case 'parameter': {
|
|
97
|
+
const owner = parent.parent?.parent;
|
|
98
|
+
return owner || memberNode;
|
|
99
|
+
}
|
|
100
|
+
case 'variable_declarator': case 'local_function_statement':
|
|
101
|
+
case 'declaration_pattern': case 'declaration_expression': case 'single_variable_designation':
|
|
102
|
+
case 'recursive_pattern': case 'var_pattern':
|
|
103
|
+
return nearest(parent, CSHARP_BLOCK_SCOPES);
|
|
104
|
+
case 'catch_declaration':
|
|
105
|
+
return parent.parent;
|
|
106
|
+
case 'foreach_statement':
|
|
107
|
+
return parent.childForFieldName('body');
|
|
108
|
+
case 'tuple_pattern': case 'parenthesized_variable_designation': case 'argument': {
|
|
109
|
+
// A deconstruction: the foreach body for its iteration
|
|
110
|
+
// variables, else the scope of the declaration or pattern
|
|
111
|
+
// that holds it (fix #419).
|
|
112
|
+
const root = deconstructionRoot(declared);
|
|
113
|
+
if (!root) return null;
|
|
114
|
+
const owner = root.parent;
|
|
115
|
+
if (owner?.type === 'foreach_statement' && contains(owner.childForFieldName('left'), declared)) {
|
|
116
|
+
return owner.childForFieldName('body');
|
|
117
|
+
}
|
|
118
|
+
return nearest(root, CSHARP_BLOCK_SCOPES);
|
|
119
|
+
}
|
|
120
|
+
case 'from_clause': case 'let_clause': case 'query_continuation':
|
|
121
|
+
return queryRange(parent, parent.endIndex);
|
|
122
|
+
case 'query_expression':
|
|
123
|
+
return queryRange(declared, declared.endIndex);
|
|
124
|
+
case 'join_into_clause':
|
|
125
|
+
return queryRange(parent, parent.parent.endIndex);
|
|
126
|
+
case 'join_clause': {
|
|
127
|
+
const equals = parent.children.find(child => child.type === 'equals');
|
|
128
|
+
const key = equals && parent.namedChildren.find(child => child.startIndex >= equals.endIndex);
|
|
129
|
+
const ranges = key ? [{ startIndex: key.startIndex, endIndex: key.endIndex }] : [];
|
|
130
|
+
if (!parent.namedChildren.some(child => child.type === 'join_into_clause')) {
|
|
131
|
+
ranges.push(queryRange(parent, parent.endIndex));
|
|
132
|
+
}
|
|
133
|
+
return { ranges };
|
|
134
|
+
}
|
|
135
|
+
default:
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
const visit = current => {
|
|
140
|
+
if (TYPE_SCOPES.has(current.type)) return;
|
|
141
|
+
if ((current.type === 'implicit_parameter' || current.type === 'identifier') && current.text === name) {
|
|
142
|
+
const parent = current.parent;
|
|
143
|
+
let declares = current.type === 'implicit_parameter';
|
|
144
|
+
if (!declares && parent) {
|
|
145
|
+
declares = (CSHARP_LOCAL_DECLARATION_NODES.has(parent.type) &&
|
|
146
|
+
sameNode(parent.childForFieldName('name'), current) && !sameNode(parent, memberNode)) ||
|
|
147
|
+
(parent.type === 'foreach_statement' && sameNode(parent.childForFieldName('left'), current)) ||
|
|
148
|
+
parent.type === 'single_variable_designation' ||
|
|
149
|
+
(parent.type === 'let_clause' && sameNode(parent.namedChild(0), current)) ||
|
|
150
|
+
rangeBindingToken(current) || !!deconstructionRoot(current);
|
|
151
|
+
}
|
|
152
|
+
if (declares) {
|
|
153
|
+
const scope = current.type === 'implicit_parameter' ? parent : scopeOf(current);
|
|
154
|
+
facts.push({ node: current, scope: snapshot(scope) });
|
|
155
|
+
} else if (parent && UNMODELED_DESIGNATION.test(parent.type)) {
|
|
156
|
+
// A same-name token in a pattern shape this lookup does not
|
|
157
|
+
// model may declare a local: every reference of the member
|
|
158
|
+
// is then undecided (fix #419).
|
|
159
|
+
facts.push({ node: current, scope: null });
|
|
160
|
+
}
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
for (const child of current.namedChildren || []) visit(child);
|
|
164
|
+
};
|
|
165
|
+
// Name lookup needs only this member's occurrences of the identifier.
|
|
166
|
+
// The AST still decides the token's role and scope; nested types keep
|
|
167
|
+
// their own bindings, just as in the member walk above.
|
|
168
|
+
const source = memberNode.text;
|
|
169
|
+
for (let at = name ? source.indexOf(name) : -1; at >= 0; at = source.indexOf(name, at + name.length)) {
|
|
170
|
+
let token = memberNode.descendantForIndex(memberNode.startIndex + at,
|
|
171
|
+
memberNode.startIndex + at + name.length);
|
|
172
|
+
if (token?.parent?.type === 'implicit_parameter' && token.parent.text === name) token = token.parent;
|
|
173
|
+
if (!token || !['identifier', 'implicit_parameter'].includes(token.type) || token.text !== name) continue;
|
|
174
|
+
let nestedType = false;
|
|
175
|
+
for (let parent = token.parent; parent && !sameNode(parent, memberNode); parent = parent.parent) {
|
|
176
|
+
if (TYPE_SCOPES.has(parent.type)) { nestedType = true; break; }
|
|
177
|
+
}
|
|
178
|
+
if (!nestedType) visit(token);
|
|
179
|
+
}
|
|
180
|
+
bindings.set(cacheKey, facts);
|
|
181
|
+
return bindingVerdict(facts, node);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Designation nodes: a token directly under one is either a modelled
|
|
185
|
+
// declaration or an undecided one. (A name directly under a subpattern is
|
|
186
|
+
// a member key or a constant, never a declaration.)
|
|
187
|
+
const UNMODELED_DESIGNATION = /_designation$/;
|
|
188
|
+
|
|
189
|
+
const TYPE_SCOPES = new Set(['class_declaration', 'struct_declaration', 'record_declaration',
|
|
190
|
+
'record_struct_declaration', 'interface_declaration']);
|
|
191
|
+
const NON_VALUE_PARENTS = new Set(['using_directive', 'qualified_name', 'alias_qualified_name',
|
|
192
|
+
'generic_name', 'type_argument_list', 'type_parameter', 'type_parameter_list', 'type_parameter_constraints_clause',
|
|
193
|
+
'attribute', 'name_equals', 'name_colon', 'argument_name', 'labeled_statement', 'goto_statement']);
|
|
194
|
+
const VALUE_PARENTS = new Set(['binary_expression', 'return_statement', 'arrow_expression_clause', 'argument',
|
|
195
|
+
'assignment_expression', 'initializer_expression', 'parenthesized_expression', 'conditional_expression',
|
|
196
|
+
'prefix_unary_expression', 'postfix_unary_expression', 'checked_expression', 'unchecked_expression',
|
|
197
|
+
'throw_expression', 'await_expression', 'interpolation', 'interpolation_alignment_clause',
|
|
198
|
+
'expression_statement', 'yield_statement', 'equals_value_clause', 'variable_declarator',
|
|
199
|
+
'anonymous_object_creation_expression', 'invocation_expression', 'member_access_expression',
|
|
200
|
+
'conditional_access_expression', 'element_access_expression', 'select_clause', 'where_clause',
|
|
201
|
+
'order_by_clause', 'group_clause', 'when_clause', 'switch_expression_arm',
|
|
202
|
+
'from_clause', 'let_clause', 'join_clause', 'foreach_statement', 'lambda_expression']);
|
|
203
|
+
|
|
204
|
+
const usingScopesByTree = new WeakMap();
|
|
205
|
+
// A conditional can wrap an otherwise ordinary file-scoped namespace.
|
|
206
|
+
function fileScopedNamespace(root) {
|
|
207
|
+
for (const child of root?.namedChildren || []) {
|
|
208
|
+
if (child.type === 'file_scoped_namespace_declaration') return child;
|
|
209
|
+
if (child.type.startsWith('preproc_')) {
|
|
210
|
+
const nested = fileScopedNamespace(child);
|
|
211
|
+
if (nested) return nested;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** The physical namespace bodies that supply this token's local usings. */
|
|
218
|
+
function usingScopesOf(node) {
|
|
219
|
+
if (!node?.tree) return [];
|
|
220
|
+
let state = usingScopesByTree.get(node.tree);
|
|
221
|
+
if (!state) { state = { nodes: new Map() }; usingScopesByTree.set(node.tree, state); }
|
|
222
|
+
const namespaces = [];
|
|
223
|
+
const types = [];
|
|
224
|
+
let scopes = [];
|
|
225
|
+
for (let current = node; current; current = current.parent) {
|
|
226
|
+
if (state.nodes.has(current.id)) { scopes = state.nodes.get(current.id); break; }
|
|
227
|
+
if (TYPE_SCOPES.has(current.type)) types.push(current);
|
|
228
|
+
if (current.type === 'namespace_declaration') namespaces.push(current);
|
|
229
|
+
if (current.type === 'compilation_unit') {
|
|
230
|
+
if (!Object.hasOwn(state, 'fileScoped')) {
|
|
231
|
+
const decl = fileScopedNamespace(current);
|
|
232
|
+
state.fileScoped = decl ? { namespace: decl.childForFieldName('name')?.text || '',
|
|
233
|
+
startIndex: decl.startIndex, endIndex: current.endIndex } : null;
|
|
234
|
+
}
|
|
235
|
+
if (state.fileScoped && node.startIndex >= state.fileScoped.startIndex) scopes = [state.fileScoped];
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
for (const declaration of namespaces.reverse()) {
|
|
240
|
+
const name = declaration.childForFieldName('name')?.text;
|
|
241
|
+
if (!name) continue;
|
|
242
|
+
const outer = scopes.at(-1)?.namespace;
|
|
243
|
+
scopes = [...scopes, { namespace: outer ? outer + '.' + name : name,
|
|
244
|
+
startIndex: declaration.startIndex, endIndex: declaration.endIndex }];
|
|
245
|
+
state.nodes.set(declaration.id, scopes);
|
|
246
|
+
}
|
|
247
|
+
for (const type of types) state.nodes.set(type.id, scopes);
|
|
248
|
+
return scopes;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function namespaceOf(node) {
|
|
252
|
+
return usingScopesOf(node).at(-1)?.namespace || '';
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const typeScopesByTree = new WeakMap();
|
|
256
|
+
function typeScope(node) {
|
|
257
|
+
let scopes = typeScopesByTree.get(node.tree);
|
|
258
|
+
if (!scopes) { scopes = new Map(); typeScopesByTree.set(node.tree, scopes); }
|
|
259
|
+
if (scopes.has(node.id)) return scopes.get(node.id);
|
|
260
|
+
const name = node.childForFieldName('name');
|
|
261
|
+
if (!name) return null;
|
|
262
|
+
const parents = [];
|
|
263
|
+
for (let p = node.parent; p; p = p.parent) {
|
|
264
|
+
if (TYPE_SCOPES.has(p.type)) parents.unshift(p.childForFieldName('name')?.text || '');
|
|
265
|
+
}
|
|
266
|
+
const list = node.namedChildren.find(child => child.type === 'type_parameter_list');
|
|
267
|
+
const scope = { name: name.text, line: name.startPosition.row + 1,
|
|
268
|
+
namespace: namespaceOf(node), enclosingType: parents.join('.'),
|
|
269
|
+
typeArity: list?.namedChildren.filter(child => child.type === 'type_parameter').length || 0 };
|
|
270
|
+
scopes.set(node.id, scope);
|
|
271
|
+
return scope;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/** Written named type at a construction, kept as AST facts for core lookup. */
|
|
275
|
+
function namedType(node, site) {
|
|
276
|
+
let current = node;
|
|
277
|
+
if (current?.type === 'nullable_type') current = current.namedChild(0);
|
|
278
|
+
if (!current) return null;
|
|
279
|
+
let qualifier = null;
|
|
280
|
+
if (current.type === 'qualified_name') {
|
|
281
|
+
qualifier = current.childForFieldName('qualifier');
|
|
282
|
+
current = current.childForFieldName('name');
|
|
283
|
+
} else if (current.type === 'alias_qualified_name') {
|
|
284
|
+
qualifier = current.childForFieldName('alias');
|
|
285
|
+
current = current.childForFieldName('name');
|
|
286
|
+
}
|
|
287
|
+
if (qualifier?.type === 'generic_name' || qualifier?.descendantsOfType('generic_name').length) return null;
|
|
288
|
+
if (current?.type === 'generic_name') current = current.namedChild(0);
|
|
289
|
+
if (current?.type !== 'identifier' || ['var', 'dynamic'].includes(current.text)) return null;
|
|
290
|
+
if (!qualifier) {
|
|
291
|
+
for (let scope = node.parent; scope; scope = scope.parent) {
|
|
292
|
+
const parameters = scope.namedChildren.find(child => child.type === 'type_parameter_list');
|
|
293
|
+
if (parameters?.namedChildren.some(parameter =>
|
|
294
|
+
parameter.childForFieldName('name')?.text === current.text || parameter.text === current.text)) return null;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return { name: current.text, node: current, text: node.text, qualifier,
|
|
298
|
+
qualifierText: qualifier?.text || null, namespace: namespaceOf(site) };
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function rangeBindingToken(node) {
|
|
302
|
+
const parent = node.parent;
|
|
303
|
+
if (!parent) return false;
|
|
304
|
+
if (parent.type === 'query_expression' && node.previousSibling?.type === 'into') return true;
|
|
305
|
+
if (parent.type === 'join_into_clause') return sameNode(parent.namedChild(0), node);
|
|
306
|
+
if (parent.type === 'join_clause') {
|
|
307
|
+
const type = parent.childForFieldName('type');
|
|
308
|
+
return sameNode(parent.namedChildren.find(child => !sameNode(child, type)), node);
|
|
309
|
+
}
|
|
310
|
+
if (parent.type === 'tuple_pattern') {
|
|
311
|
+
for (let owner = parent.parent; owner; owner = owner.parent) {
|
|
312
|
+
if (owner.type === 'foreach_statement') {
|
|
313
|
+
const left = owner.childForFieldName('left');
|
|
314
|
+
return left && left.startIndex <= node.startIndex && node.endIndex <= left.endIndex;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function declaresToken(node) {
|
|
322
|
+
const parent = node.parent;
|
|
323
|
+
if (!parent) return false;
|
|
324
|
+
if (rangeBindingToken(node)) return true;
|
|
325
|
+
if (sameNode(parent.childForFieldName('type'), node) ||
|
|
326
|
+
sameNode(parent.childForFieldName('returns'), node)) return true;
|
|
327
|
+
if (sameNode(parent.childForFieldName('name'), node) &&
|
|
328
|
+
(parent.type.endsWith('_declaration') || CSHARP_LOCAL_DECLARATION_NODES.has(parent.type))) return true;
|
|
329
|
+
return parent.type === 'implicit_parameter' || parent.type === 'single_variable_designation' ||
|
|
330
|
+
(parent.type === 'foreach_statement' && sameNode(parent.childForFieldName('left'), node)) ||
|
|
331
|
+
(parent.type === 'let_clause' && sameNode(parent.namedChild(0), node)) ||
|
|
332
|
+
// Deconstruction designations declare locals (fix #419).
|
|
333
|
+
!!deconstructionRoot(node);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// A primary-constructor parameter supplies instance initializers and base
|
|
337
|
+
// arguments. Inside ordinary member bodies, the member lookup comes first.
|
|
338
|
+
function primaryParameterInInitializer(node, type, name) {
|
|
339
|
+
const list = type?.namedChildren.find(child => child.type === 'parameter_list');
|
|
340
|
+
if (!list?.namedChildren.some(parameter => parameter.childForFieldName('name')?.text === name)) return false;
|
|
341
|
+
for (let current = node; current?.parent && !sameNode(current.parent, type); current = current.parent) {
|
|
342
|
+
const parent = current.parent;
|
|
343
|
+
if (parent.type === 'base_list' || parent.type === 'primary_constructor_base_type') return true;
|
|
344
|
+
if (parent.type === 'property_declaration' &&
|
|
345
|
+
sameNode(parent.childForFieldName('value'), current) && current.type !== 'arrow_expression_clause') return true;
|
|
346
|
+
if (parent.type === 'variable_declarator' && parent.parent?.parent?.type === 'field_declaration' &&
|
|
347
|
+
!sameNode(parent.childForFieldName('name'), current)) return true;
|
|
348
|
+
}
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// C# 14 makes a bare `field` in a property body a backing-field keyword.
|
|
353
|
+
// Earlier versions can bind the same AST identifier to an ordinary member.
|
|
354
|
+
function possibleBackingField(node) {
|
|
355
|
+
if (node.text !== 'field') return false;
|
|
356
|
+
let accessor = false;
|
|
357
|
+
for (let current = node.parent; current; current = current.parent) {
|
|
358
|
+
if (current.type === 'accessor_declaration') {
|
|
359
|
+
accessor = ['get', 'set', 'init'].includes(current.childForFieldName('name')?.text);
|
|
360
|
+
}
|
|
361
|
+
if (current.type === 'property_declaration') {
|
|
362
|
+
const value = current.childForFieldName('value');
|
|
363
|
+
return accessor || (value?.type === 'arrow_expression_clause' &&
|
|
364
|
+
value.startIndex <= node.startIndex && node.endIndex <= value.endIndex);
|
|
365
|
+
}
|
|
366
|
+
if (current.type === 'indexer_declaration' || TYPE_SCOPES.has(current.type)) return false;
|
|
367
|
+
}
|
|
368
|
+
return false;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Property-bearing expression shapes whose syntax has no simple receiver.
|
|
373
|
+
* The core resolves owners; this adapter reports only grammar and scopes.
|
|
374
|
+
*/
|
|
375
|
+
function accessorReferenceShape(tree, usage, implicitCreationTarget) {
|
|
376
|
+
if (!tree || !Number.isInteger(usage.column)) return null;
|
|
377
|
+
const node = tree.rootNode.descendantForPosition({ row: usage.line - 1, column: usage.column });
|
|
378
|
+
if (!node || node.type !== 'identifier') return null;
|
|
379
|
+
const parent = node.parent;
|
|
380
|
+
if (!parent) return 'none';
|
|
381
|
+
if (parent.type === 'name_equals' || parent.type === 'attribute_argument' && node.nextSibling?.type === '=' ||
|
|
382
|
+
parent.type === 'assignment_expression' && sameNode(parent.childForFieldName('left'), node) &&
|
|
383
|
+
parent.parent?.type === 'attribute_argument') {
|
|
384
|
+
let owner = parent;
|
|
385
|
+
while (owner && owner.type !== 'attribute' && !TYPE_SCOPES.has(owner.type)) owner = owner.parent;
|
|
386
|
+
// An attribute's named field/property belongs to the attribute,
|
|
387
|
+
// never to the class carrying it. Its abbreviated type name and
|
|
388
|
+
// constructor binding need a separate ownership proof.
|
|
389
|
+
if (owner?.type === 'attribute') return { kind: 'unresolved', node, uncertainRole: true };
|
|
390
|
+
}
|
|
391
|
+
let pattern = parent;
|
|
392
|
+
while (pattern?.type === 'member_access_expression') pattern = pattern.parent;
|
|
393
|
+
if (pattern?.type === 'subpattern' && pattern.children.some(child => child.type === ':')) {
|
|
394
|
+
return { kind: 'pattern', accessKind: 'read', node };
|
|
395
|
+
}
|
|
396
|
+
if (parent.type === 'member_access_expression' && sameNode(parent.childForFieldName('name'), node)) {
|
|
397
|
+
return { kind: 'member', node };
|
|
398
|
+
}
|
|
399
|
+
if (parent.type === 'assignment_expression' && sameNode(parent.childForFieldName('left'), node) &&
|
|
400
|
+
parent.parent?.type === 'initializer_expression') {
|
|
401
|
+
const holder = parent.parent.parent;
|
|
402
|
+
const type = holder?.type === 'object_creation_expression' ? holder.childForFieldName('type')
|
|
403
|
+
: holder?.type === 'implicit_object_creation_expression' ? implicitCreationTarget(holder) : null;
|
|
404
|
+
const value = parent.childForFieldName('right');
|
|
405
|
+
return { kind: 'initializer', node, type: namedType(type, node),
|
|
406
|
+
accessKind: value?.type === 'initializer_expression' ? 'read' : 'write' };
|
|
407
|
+
}
|
|
408
|
+
if (parent.type === 'with_initializer' && node.nextSibling?.type === '=') {
|
|
409
|
+
return { kind: 'initializer', node, type: null, accessKind: 'write' };
|
|
410
|
+
}
|
|
411
|
+
if (parent.type === 'subpattern' && node.nextSibling?.type === ':') {
|
|
412
|
+
return { kind: 'pattern', node, accessKind: 'read' };
|
|
413
|
+
}
|
|
414
|
+
if (NON_VALUE_PARENTS.has(parent.type) || declaresToken(node)) return 'none';
|
|
415
|
+
if (possibleBackingField(node)) return { kind: 'unresolved', node, uncertainRole: true };
|
|
416
|
+
// Unknown member syntax carries no implicit-receiver evidence. In
|
|
417
|
+
// particular, a conditional member name cannot bind a local or `value`.
|
|
418
|
+
if (!VALUE_PARENTS.has(parent.type)) return { kind: 'unresolved', node, uncertainRole: true };
|
|
419
|
+
const types = [];
|
|
420
|
+
let member = null;
|
|
421
|
+
for (let current = parent; current; current = current.parent) {
|
|
422
|
+
if (TYPE_SCOPES.has(current.type)) types.push(current);
|
|
423
|
+
else if (types.length === 0 && current.parent?.type === 'declaration_list') member = current;
|
|
424
|
+
// Setter/init and event accessor parameters are implicit in the AST.
|
|
425
|
+
if (current.type === 'accessor_declaration' && node.text === 'value' &&
|
|
426
|
+
['set', 'init', 'add', 'remove'].includes(current.childForFieldName('name')?.text)) return 'none';
|
|
427
|
+
}
|
|
428
|
+
if (types.length === 0 && node.text === 'args') return 'none';
|
|
429
|
+
const local = csharpLocalBinding(member || (types.length === 0 ? tree.rootNode : parent), node, node.text);
|
|
430
|
+
if (local === 'local' || primaryParameterInInitializer(node, types[0], node.text)) return 'none';
|
|
431
|
+
const projection = parent.type === 'anonymous_object_creation_expression';
|
|
432
|
+
if (projection && node.nextSibling?.type === '=') return 'none';
|
|
433
|
+
const scopes = types.map(typeScope).filter(Boolean);
|
|
434
|
+
return { kind: 'implicit', scopes, node, namespace: scopes[0]?.namespace ?? namespaceOf(node),
|
|
435
|
+
uncertainRole: local === 'unknown' || parent.type === 'member_access_expression',
|
|
436
|
+
inferredName: projection };
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
module.exports = { csharpLocalBinding, accessorReferenceShape, namedType, usingScopesOf, fileScopedNamespace, deconstructionRoot };
|