ucn 5.0.6 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/ucn/SKILL.md +12 -5
- package/.claude/skills/ucn/references/commands.md +2 -2
- package/.claude/skills/ucn/references/trust-contract.md +3 -2
- package/README.md +31 -14
- package/core/analysis.js +66 -1
- package/core/bridge.js +2 -1
- package/core/cache.js +64 -8
- package/core/callers.js +1722 -90
- package/core/graph-build.js +6 -0
- package/core/index-ir.js +16 -5
- package/core/ir.js +50 -3
- package/core/output/analysis.js +25 -0
- package/core/output/refactoring.js +1 -1
- package/core/output/shared.js +2 -2
- package/core/project.js +32 -0
- package/core/search.js +9 -0
- package/core/verify.js +1084 -36
- package/languages/c-family.js +231 -9
- package/languages/csharp.js +14 -3
- package/languages/go.js +473 -71
- package/languages/javascript.js +288 -21
- package/languages/python.js +443 -97
- package/languages/rust.js +87 -4
- package/languages/utils.js +11 -0
- package/mcp/server.js +100 -104
- package/mcp/stdio-server.js +296 -0
- package/package.json +10 -8
package/languages/c-family.js
CHANGED
|
@@ -313,6 +313,7 @@ const allSourceTreeBySelected = new WeakMap();
|
|
|
313
313
|
// name merely to rediscover the same handful of macro definitions. Trees are
|
|
314
314
|
// immutable, so retain the exact AST node set without retaining dead trees.
|
|
315
315
|
const macroDefinitionsByTree = new WeakMap();
|
|
316
|
+
const macroParamEffectsByDefinition = new WeakMap();
|
|
316
317
|
|
|
317
318
|
function macroDefinitionNodes(tree) {
|
|
318
319
|
const cached = macroDefinitionsByTree.get(tree);
|
|
@@ -964,6 +965,24 @@ function isTemplateDependentCallable(node) {
|
|
|
964
965
|
return false;
|
|
965
966
|
}
|
|
966
967
|
|
|
968
|
+
// `template <> bool f<bool>(...)` is a FULL specialization: the same compiler
|
|
969
|
+
// symbol as its primary template, selected by substitution rather than
|
|
970
|
+
// overload resolution (fix #299). Every enclosing template head must be
|
|
971
|
+
// empty — one non-empty parameter list means a member of a class template
|
|
972
|
+
// (partial-specialization territory for classes; ordinary dependence here).
|
|
973
|
+
function isFullSpecializationCallable(node) {
|
|
974
|
+
let sawTemplateHead = false;
|
|
975
|
+
for (let parent = node?.parent; parent; parent = parent.parent) {
|
|
976
|
+
if (parent.type === 'template_declaration') {
|
|
977
|
+
const params = parent.childForFieldName('parameters');
|
|
978
|
+
if (!params || params.namedChildCount > 0) return false;
|
|
979
|
+
sawTemplateHead = true;
|
|
980
|
+
}
|
|
981
|
+
if (parent.type === 'translation_unit') break;
|
|
982
|
+
}
|
|
983
|
+
return sawTemplateHead;
|
|
984
|
+
}
|
|
985
|
+
|
|
967
986
|
// Full type text for a NAMED parameter: the parameter's own text with the
|
|
968
987
|
// name removed — `const char *s` → `const char *`, `int **pp` → `int **`,
|
|
969
988
|
// `void (*cb)(int)` → `void (*)(int)`. Reading around the identifier keeps
|
|
@@ -1217,6 +1236,9 @@ function memberFromNode(node, className, access, lines, mode) {
|
|
|
1217
1236
|
...(mode === 'cpp' && isTemplateDependentCallable(node) && {
|
|
1218
1237
|
templateDependent: true,
|
|
1219
1238
|
}),
|
|
1239
|
+
...(mode === 'cpp' && isFullSpecializationCallable(node) && {
|
|
1240
|
+
isSpecialization: true,
|
|
1241
|
+
}),
|
|
1220
1242
|
...(mode === 'cpp' && cLanguageLinkage(node) && {
|
|
1221
1243
|
linkage: cLanguageLinkage(node),
|
|
1222
1244
|
}),
|
|
@@ -1516,6 +1538,9 @@ function findFunctionsInTree(code, tree, mode, sourceLines = null) {
|
|
|
1516
1538
|
...(mode === 'cpp' && isTemplateDependentCallable(node) && {
|
|
1517
1539
|
templateDependent: true,
|
|
1518
1540
|
}),
|
|
1541
|
+
...(mode === 'cpp' && isFullSpecializationCallable(node) && {
|
|
1542
|
+
isSpecialization: true,
|
|
1543
|
+
}),
|
|
1519
1544
|
...(returnedConcreteType && { returnedConcreteType }),
|
|
1520
1545
|
...(mode === 'cpp' && cLanguageLinkage(node) && {
|
|
1521
1546
|
linkage: cLanguageLinkage(node),
|
|
@@ -1658,7 +1683,112 @@ function findStateObjects(code, parser) {
|
|
|
1658
1683
|
item => `${item.name}:${item.startLine}`) : primary;
|
|
1659
1684
|
}
|
|
1660
1685
|
|
|
1661
|
-
function
|
|
1686
|
+
function collectMacroParameterEffects(nestedTree, parameters) {
|
|
1687
|
+
const effects = [];
|
|
1688
|
+
traverseTree(nestedTree.rootNode, node => {
|
|
1689
|
+
// Token-paste after a global qualifier (`::_##call`) is not C++
|
|
1690
|
+
// until preprocessing. tree-sitter preserves the `#call` token as
|
|
1691
|
+
// a preprocessor node next to an AST global qualified identifier;
|
|
1692
|
+
// that exact shape still proves the substituted callable is
|
|
1693
|
+
// globally qualified.
|
|
1694
|
+
if (node.type === 'preproc_directive' &&
|
|
1695
|
+
node.text.startsWith('#')) {
|
|
1696
|
+
const paramIndex = parameters.indexOf(node.text.slice(1));
|
|
1697
|
+
let globalSibling = false;
|
|
1698
|
+
let container = node.parent;
|
|
1699
|
+
for (let hops = 0; container && hops < 3 && !globalSibling;
|
|
1700
|
+
hops++, container = container.parent) {
|
|
1701
|
+
const stack = [...(container.namedChildren || [])];
|
|
1702
|
+
while (stack.length > 0 && !globalSibling) {
|
|
1703
|
+
const sibling = stack.pop();
|
|
1704
|
+
if (sibling === node) continue;
|
|
1705
|
+
if (sibling.type === 'qualified_identifier' &&
|
|
1706
|
+
sibling.children?.[0]?.type === '::') {
|
|
1707
|
+
globalSibling = true;
|
|
1708
|
+
break;
|
|
1709
|
+
}
|
|
1710
|
+
for (const child of sibling.namedChildren || []) {
|
|
1711
|
+
stack.push(child);
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
if (paramIndex >= 0 && globalSibling) {
|
|
1716
|
+
effects.push({
|
|
1717
|
+
paramIndex,
|
|
1718
|
+
kind: 'qualified',
|
|
1719
|
+
qualifier: 'global',
|
|
1720
|
+
});
|
|
1721
|
+
}
|
|
1722
|
+
return true;
|
|
1723
|
+
}
|
|
1724
|
+
if (node.type !== 'identifier') return true;
|
|
1725
|
+
const paramIndex = parameters.indexOf(node.text);
|
|
1726
|
+
if (paramIndex < 0) return true;
|
|
1727
|
+
const parent = node.parent;
|
|
1728
|
+
if (parent?.type === 'qualified_identifier' &&
|
|
1729
|
+
parent.childForFieldName('name') === node) {
|
|
1730
|
+
const scope = parent.childForFieldName('scope');
|
|
1731
|
+
effects.push({
|
|
1732
|
+
paramIndex,
|
|
1733
|
+
kind: 'qualified',
|
|
1734
|
+
qualifier: scope?.text || 'global',
|
|
1735
|
+
});
|
|
1736
|
+
return true;
|
|
1737
|
+
}
|
|
1738
|
+
if (parent?.type === 'argument_list' &&
|
|
1739
|
+
parent.parent?.type === 'call_expression') {
|
|
1740
|
+
const wrapper = callIdentity(
|
|
1741
|
+
parent.parent.childForFieldName('function'));
|
|
1742
|
+
if (!wrapper.name || !isMacroToken(wrapper.name)) return true;
|
|
1743
|
+
const args = (parent.namedChildren || [])
|
|
1744
|
+
.filter(child => !child.type.endsWith('comment'));
|
|
1745
|
+
const argIndex = args.findIndex(argument =>
|
|
1746
|
+
argument.startIndex <= node.startIndex &&
|
|
1747
|
+
node.endIndex <= argument.endIndex);
|
|
1748
|
+
if (argIndex >= 0) {
|
|
1749
|
+
effects.push({
|
|
1750
|
+
paramIndex,
|
|
1751
|
+
kind: 'forwarded',
|
|
1752
|
+
macro: wrapper.name,
|
|
1753
|
+
argIndex,
|
|
1754
|
+
});
|
|
1755
|
+
}
|
|
1756
|
+
}
|
|
1757
|
+
return true;
|
|
1758
|
+
});
|
|
1759
|
+
return effects;
|
|
1760
|
+
}
|
|
1761
|
+
|
|
1762
|
+
function macroParameterEffects(definitionNode, valueNode, paramsNode, parser) {
|
|
1763
|
+
if (definitionNode && macroParamEffectsByDefinition.has(definitionNode)) {
|
|
1764
|
+
return macroParamEffectsByDefinition.get(definitionNode);
|
|
1765
|
+
}
|
|
1766
|
+
if (!valueNode || !paramsNode || !parser) return [];
|
|
1767
|
+
const parameters = (paramsNode.namedChildren || [])
|
|
1768
|
+
.filter(child => child.type === 'identifier')
|
|
1769
|
+
.map(child => child.text);
|
|
1770
|
+
if (parameters.length === 0) return [];
|
|
1771
|
+
const body = valueNode.text.replace(/\\(?=\r?\n)/g, ' ');
|
|
1772
|
+
// Cheap parse-avoidance only; every semantic decision below comes from
|
|
1773
|
+
// the replacement list's tree. Ordinary value/punctuation macros need no
|
|
1774
|
+
// extra native tree.
|
|
1775
|
+
if (!body.includes('::') &&
|
|
1776
|
+
!/[A-Z_][A-Z0-9_]*\s*\(/.test(body)) return [];
|
|
1777
|
+
const prefix = 'void __ucn_macro_effect__(void) {\n';
|
|
1778
|
+
const synthetic = `${prefix}${body}\n;}`;
|
|
1779
|
+
const nestedTree = safeParse(parser, synthetic, undefined, PARSE_OPTIONS);
|
|
1780
|
+
try {
|
|
1781
|
+
const effects = collectMacroParameterEffects(nestedTree, parameters);
|
|
1782
|
+
if (definitionNode) {
|
|
1783
|
+
macroParamEffectsByDefinition.set(definitionNode, effects);
|
|
1784
|
+
}
|
|
1785
|
+
return effects;
|
|
1786
|
+
} finally {
|
|
1787
|
+
nestedTree.delete?.();
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
function findMacrosInTree(tree, lines, parser) {
|
|
1662
1792
|
const macros = [];
|
|
1663
1793
|
for (const node of macroDefinitionNodes(tree)) {
|
|
1664
1794
|
const nameNode = node.childForFieldName('name') ||
|
|
@@ -1684,6 +1814,17 @@ function findMacrosInTree(tree, lines) {
|
|
|
1684
1814
|
: undefined,
|
|
1685
1815
|
modifiers: [],
|
|
1686
1816
|
functionLike: node.type === 'preproc_function_def',
|
|
1817
|
+
...(() => {
|
|
1818
|
+
const effects = macroParameterEffects(
|
|
1819
|
+
node,
|
|
1820
|
+
node.childForFieldName('value') ||
|
|
1821
|
+
(node.namedChildren || []).find(child =>
|
|
1822
|
+
child.type === 'preproc_arg'),
|
|
1823
|
+
paramsNode,
|
|
1824
|
+
parser);
|
|
1825
|
+
return effects.length > 0
|
|
1826
|
+
? { macroParamEffects: effects } : {};
|
|
1827
|
+
})(),
|
|
1687
1828
|
docstring: extractJSDocstring(lines, startLine),
|
|
1688
1829
|
});
|
|
1689
1830
|
}
|
|
@@ -1693,10 +1834,10 @@ function findMacrosInTree(tree, lines) {
|
|
|
1693
1834
|
function findMacros(code, parser) {
|
|
1694
1835
|
const tree = parseTree(parser, code);
|
|
1695
1836
|
const lines = code.split('\n');
|
|
1696
|
-
const primary = findMacrosInTree(tree, lines);
|
|
1837
|
+
const primary = findMacrosInTree(tree, lines, parser);
|
|
1697
1838
|
const literal = literalRecoveryTree(parser, code, tree);
|
|
1698
1839
|
return literal ? mergeExtracted(primary,
|
|
1699
|
-
findMacrosInTree(literal, lines),
|
|
1840
|
+
findMacrosInTree(literal, lines, parser),
|
|
1700
1841
|
item => `${item.name}:${item.startLine}:${item.functionLike ? 1 : 0}`) : primary;
|
|
1701
1842
|
}
|
|
1702
1843
|
|
|
@@ -1789,6 +1930,23 @@ function buildVariableTypes(tree) {
|
|
|
1789
1930
|
// block declaration.
|
|
1790
1931
|
const memberReceiverUses = [];
|
|
1791
1932
|
const ambiguousDirectInitializers = [];
|
|
1933
|
+
const declaratorStaticType = (type, declarator) => {
|
|
1934
|
+
// The declaration's type node carries only the base type. Preserve
|
|
1935
|
+
// array rank from the AST declarator for overload arguments:
|
|
1936
|
+
// `wchar_t format_str[]; runtime(format_str)` passes a wide-character
|
|
1937
|
+
// array (and decays to wchar_t*), not a scalar wchar_t. Receiver
|
|
1938
|
+
// typing continues to use the base `type`; only static argument shape
|
|
1939
|
+
// consumes this fuller spelling.
|
|
1940
|
+
let arrays = 0;
|
|
1941
|
+
const stack = [declarator];
|
|
1942
|
+
while (stack.length > 0) {
|
|
1943
|
+
const current = stack.pop();
|
|
1944
|
+
if (!current) continue;
|
|
1945
|
+
if (current.type === 'array_declarator') arrays++;
|
|
1946
|
+
for (const child of current.namedChildren || []) stack.push(child);
|
|
1947
|
+
}
|
|
1948
|
+
return arrays > 0 ? `${type}${'[]'.repeat(arrays)}` : type;
|
|
1949
|
+
};
|
|
1792
1950
|
const addBindings = (node, type, pointeeType, scope, declarators) => {
|
|
1793
1951
|
for (const declarator of declarators) {
|
|
1794
1952
|
const identity = declaratorIdentity(declarator);
|
|
@@ -1796,6 +1954,7 @@ function buildVariableTypes(tree) {
|
|
|
1796
1954
|
bindings.push({
|
|
1797
1955
|
name: identity.name,
|
|
1798
1956
|
type,
|
|
1957
|
+
staticType: declaratorStaticType(type, declarator),
|
|
1799
1958
|
...(pointeeType && { pointeeType }),
|
|
1800
1959
|
declaredAt: node.type === 'parameter_declaration'
|
|
1801
1960
|
? scope.startIndex : declarator.startIndex,
|
|
@@ -1893,6 +2052,7 @@ function buildVariableTypes(tree) {
|
|
|
1893
2052
|
};
|
|
1894
2053
|
return {
|
|
1895
2054
|
get: (name, atNode) => resolveBinding(name, atNode)?.type,
|
|
2055
|
+
getStatic: (name, atNode) => resolveBinding(name, atNode)?.staticType,
|
|
1896
2056
|
getPointee: (name, atNode) =>
|
|
1897
2057
|
resolveBinding(name, atNode)?.pointeeType,
|
|
1898
2058
|
has: (name, atNode) => resolveBinding(name, atNode) !== undefined,
|
|
@@ -1963,7 +2123,8 @@ function staticArgumentKind(node, variableTypes) {
|
|
|
1963
2123
|
if (node.type === 'true' || node.type === 'false') return 'bool';
|
|
1964
2124
|
if (node.type === 'null' || node.type === 'nullptr') return 'null';
|
|
1965
2125
|
if (node.type === 'identifier') {
|
|
1966
|
-
const type = variableTypes?.
|
|
2126
|
+
const type = variableTypes?.getStatic(node.text, node) ||
|
|
2127
|
+
variableTypes?.get(node.text, node);
|
|
1967
2128
|
return type ? `type:${type}` : 'expr';
|
|
1968
2129
|
}
|
|
1969
2130
|
if (node.type === 'compound_literal_expression') {
|
|
@@ -1998,6 +2159,18 @@ function staticArgumentKind(node, variableTypes) {
|
|
|
1998
2159
|
const type = typeName(typeNode);
|
|
1999
2160
|
if (type) return `type:${type}`;
|
|
2000
2161
|
}
|
|
2162
|
+
// Bare-identifier producers are name-resolvable (fix #299B): mark
|
|
2163
|
+
// them `bcall:` so the overload discipline can type the argument
|
|
2164
|
+
// from the producer's declared return type. A local callable
|
|
2165
|
+
// variable shadows the project name — those record plain 'expr'
|
|
2166
|
+
// (the #203 localShadow rule at kind-recording time). Member,
|
|
2167
|
+
// qualified, and template-explicit producers keep `call:NAME`.
|
|
2168
|
+
if (identity.name && !identity.isMethod && !identity.isPathCall &&
|
|
2169
|
+
identity.nameNode && IDENTIFIER_NODES.has(identity.nameNode.type) &&
|
|
2170
|
+
fnNode && IDENTIFIER_NODES.has(fnNode.type)) {
|
|
2171
|
+
if (variableTypes?.get(identity.name, node)) return 'expr';
|
|
2172
|
+
return `bcall:${identity.name}`;
|
|
2173
|
+
}
|
|
2001
2174
|
return identity.name ? `call:${identity.name}` : 'expr';
|
|
2002
2175
|
}
|
|
2003
2176
|
return 'expr';
|
|
@@ -2202,6 +2375,46 @@ function fieldReceiverPath(node) {
|
|
|
2202
2375
|
return { root: base.root, fields: [...base.fields, field.text] };
|
|
2203
2376
|
}
|
|
2204
2377
|
|
|
2378
|
+
/** Macro invocations whose argument expression contains this call node. */
|
|
2379
|
+
function enclosingMacroArguments(node) {
|
|
2380
|
+
const wrappers = [];
|
|
2381
|
+
let current = node;
|
|
2382
|
+
let hops = 0;
|
|
2383
|
+
while (current?.parent && hops++ < 24) {
|
|
2384
|
+
const argumentsNode = current.parent;
|
|
2385
|
+
const outerCall = argumentsNode?.type === 'argument_list'
|
|
2386
|
+
? argumentsNode.parent : null;
|
|
2387
|
+
if (outerCall?.type === 'call_expression') {
|
|
2388
|
+
const wrapper = callIdentity(
|
|
2389
|
+
outerCall.childForFieldName('function'));
|
|
2390
|
+
if (wrapper.name && isMacroToken(wrapper.name)) {
|
|
2391
|
+
const args = (argumentsNode.namedChildren || [])
|
|
2392
|
+
.filter(child => !child.type.endsWith('comment'));
|
|
2393
|
+
const argIndex = args.findIndex(argument =>
|
|
2394
|
+
argument.startIndex <= node.startIndex &&
|
|
2395
|
+
node.endIndex <= argument.endIndex);
|
|
2396
|
+
if (argIndex >= 0) {
|
|
2397
|
+
wrappers.push({ name: wrapper.name, argIndex });
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
current = outerCall;
|
|
2401
|
+
continue;
|
|
2402
|
+
}
|
|
2403
|
+
// Once the walk leaves an expression, no outer macro invocation can
|
|
2404
|
+
// contain this call. Stopping here keeps the common non-macro path
|
|
2405
|
+
// constant-depth instead of climbing every call to the translation
|
|
2406
|
+
// unit (material on template-heavy fmt headers).
|
|
2407
|
+
if (/(?:statement|declaration|definition)$/.test(
|
|
2408
|
+
argumentsNode.type) ||
|
|
2409
|
+
argumentsNode.type === 'translation_unit' ||
|
|
2410
|
+
argumentsNode.type === 'init_declarator') {
|
|
2411
|
+
break;
|
|
2412
|
+
}
|
|
2413
|
+
current = current.parent;
|
|
2414
|
+
}
|
|
2415
|
+
return wrappers;
|
|
2416
|
+
}
|
|
2417
|
+
|
|
2205
2418
|
function findCallsInTree(code, parser, _options = {}, existingTree = null,
|
|
2206
2419
|
includeMacroBodies = true) {
|
|
2207
2420
|
const tree = existingTree || parseTree(parser, code);
|
|
@@ -2281,6 +2494,7 @@ function findCallsInTree(code, parser, _options = {}, existingTree = null,
|
|
|
2281
2494
|
: undefined;
|
|
2282
2495
|
const assignedTo = assignmentTargetOf(node);
|
|
2283
2496
|
const compileTimeOnly = compileTimeOnlyContext(node);
|
|
2497
|
+
const macroArguments = enclosingMacroArguments(node);
|
|
2284
2498
|
calls.push({
|
|
2285
2499
|
name: identity.name,
|
|
2286
2500
|
line: identity.nameNode?.startPosition.row + 1 || node.startPosition.row + 1,
|
|
@@ -2295,6 +2509,7 @@ function findCallsInTree(code, parser, _options = {}, existingTree = null,
|
|
|
2295
2509
|
explicitTemplateCall: true,
|
|
2296
2510
|
}),
|
|
2297
2511
|
...(compileTimeOnly && { compileTimeOnly }),
|
|
2512
|
+
...(macroArguments.length > 0 && { macroArguments }),
|
|
2298
2513
|
...(directReceiverType && { receiverType: directReceiverType }),
|
|
2299
2514
|
...(receiverCall && {
|
|
2300
2515
|
receiverCall,
|
|
@@ -2474,9 +2689,10 @@ function findMacroBodyCalls(tree, code, parser, onlyName = null) {
|
|
|
2474
2689
|
if (onlyName && !valueNode.text.includes(onlyName)) continue;
|
|
2475
2690
|
const paramsNode = node.childForFieldName('parameters') ||
|
|
2476
2691
|
(node.namedChildren || []).find(child => child.type === 'preproc_params');
|
|
2477
|
-
const
|
|
2692
|
+
const parameterNames = (paramsNode?.namedChildren || [])
|
|
2478
2693
|
.filter(child => child.type === 'identifier')
|
|
2479
|
-
.map(child => child.text)
|
|
2694
|
+
.map(child => child.text);
|
|
2695
|
+
const parameters = new Set(parameterNames);
|
|
2480
2696
|
// Replace only the continuation backslash. Keeping the newline and
|
|
2481
2697
|
// every other byte makes source-line/column and span remapping exact.
|
|
2482
2698
|
const body = valueNode.text.replace(/\\(?=\r?\n)/g, ' ');
|
|
@@ -2490,6 +2706,12 @@ function findMacroBodyCalls(tree, code, parser, onlyName = null) {
|
|
|
2490
2706
|
const nestedTree = safeParse(parser, synthetic, undefined, PARSE_OPTIONS);
|
|
2491
2707
|
let nested;
|
|
2492
2708
|
try {
|
|
2709
|
+
macroParamEffectsByDefinition.set(
|
|
2710
|
+
node,
|
|
2711
|
+
body.includes('::') ||
|
|
2712
|
+
/[A-Z_][A-Z0-9_]*\s*\(/.test(body)
|
|
2713
|
+
? collectMacroParameterEffects(nestedTree, parameterNames)
|
|
2714
|
+
: []);
|
|
2493
2715
|
nested = findCallsInCode(synthetic, parser, {}, nestedTree, false);
|
|
2494
2716
|
} finally {
|
|
2495
2717
|
nestedTree.delete?.();
|
|
@@ -2725,10 +2947,10 @@ function parse(code, parser, mode, options = {}) {
|
|
|
2725
2947
|
item => `${item.name}:${item.startLine}`)
|
|
2726
2948
|
: findStateObjectsInTree(tree, lines),
|
|
2727
2949
|
macros: literal
|
|
2728
|
-
? mergeExtracted(findMacrosInTree(tree, lines),
|
|
2729
|
-
findMacrosInTree(literal, lines),
|
|
2950
|
+
? mergeExtracted(findMacrosInTree(tree, lines, parser),
|
|
2951
|
+
findMacrosInTree(literal, lines, parser),
|
|
2730
2952
|
item => `${item.name}:${item.startLine}:${item.functionLike ? 1 : 0}`)
|
|
2731
|
-
: findMacrosInTree(tree, lines),
|
|
2953
|
+
: findMacrosInTree(tree, lines, parser),
|
|
2732
2954
|
imports,
|
|
2733
2955
|
exports: [
|
|
2734
2956
|
...functions
|
package/languages/csharp.js
CHANGED
|
@@ -1155,10 +1155,21 @@ function findCallsInCode(code, parser) {
|
|
|
1155
1155
|
: functionNode?.type === 'conditional_access_expression'
|
|
1156
1156
|
? functionNode.childForFieldName('condition') || functionNode.namedChild(0)
|
|
1157
1157
|
: null;
|
|
1158
|
-
const receiverTypeInfo = receiverTypeFromNode(receiverNode, variableTypes) ||
|
|
1159
|
-
normalizeReceiverType(receiverRoot && variableTypes.get(receiverRoot));
|
|
1160
|
-
const receiverType = receiverTypeInfo?.name;
|
|
1161
1158
|
const unwrappedReceiverNode = unwrapReceiverNode(receiverNode);
|
|
1159
|
+
// A root variable's type is the receiver type only for a direct
|
|
1160
|
+
// `value.Method()` call. For `value.Property.Method()` the static
|
|
1161
|
+
// receiver type is the property's declared type, not `value`'s
|
|
1162
|
+
// type. Preserve that shape as a field path for query-time
|
|
1163
|
+
// declaration walking; collapsing it to the root class falsely
|
|
1164
|
+
// confirms sibling overrides (Newtonsoft JProperty.Value is a
|
|
1165
|
+
// JToken, not a JProperty).
|
|
1166
|
+
const receiverTypeInfo = receiverTypeFromNode(
|
|
1167
|
+
receiverNode, variableTypes) ||
|
|
1168
|
+
(unwrappedReceiverNode?.type === 'identifier'
|
|
1169
|
+
? normalizeReceiverType(
|
|
1170
|
+
receiverRoot && variableTypes.get(receiverRoot))
|
|
1171
|
+
: null);
|
|
1172
|
+
const receiverType = receiverTypeInfo?.name;
|
|
1162
1173
|
const receiverCastThis = receiverCastIsThis(receiverNode);
|
|
1163
1174
|
const receiverIsTypeQualified = !!(identity.isMethod &&
|
|
1164
1175
|
unwrappedReceiverNode?.type === 'identifier' &&
|