projscan 1.1.1 → 1.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/README.md +9 -5
- package/dist/cli/commands/coverage.js +4 -0
- package/dist/cli/commands/coverage.js.map +1 -1
- package/dist/cli/commands/prDiff.js +4 -0
- package/dist/cli/commands/prDiff.js.map +1 -1
- package/dist/core/ast.d.ts +14 -0
- package/dist/core/ast.js +30 -8
- package/dist/core/ast.js.map +1 -1
- package/dist/core/codeGraph.js +68 -3
- package/dist/core/codeGraph.js.map +1 -1
- package/dist/core/languages/LanguageAdapter.d.ts +1 -1
- package/dist/core/languages/csharpAdapter.d.ts +2 -0
- package/dist/core/languages/csharpAdapter.js +148 -0
- package/dist/core/languages/csharpAdapter.js.map +1 -0
- package/dist/core/languages/csharpCallSites.d.ts +22 -0
- package/dist/core/languages/csharpCallSites.js +73 -0
- package/dist/core/languages/csharpCallSites.js.map +1 -0
- package/dist/core/languages/csharpCyclomatic.d.ts +23 -0
- package/dist/core/languages/csharpCyclomatic.js +61 -0
- package/dist/core/languages/csharpCyclomatic.js.map +1 -0
- package/dist/core/languages/csharpExports.d.ts +13 -0
- package/dist/core/languages/csharpExports.js +69 -0
- package/dist/core/languages/csharpExports.js.map +1 -0
- package/dist/core/languages/csharpFunctions.d.ts +24 -0
- package/dist/core/languages/csharpFunctions.js +170 -0
- package/dist/core/languages/csharpFunctions.js.map +1 -0
- package/dist/core/languages/csharpImports.d.ts +30 -0
- package/dist/core/languages/csharpImports.js +90 -0
- package/dist/core/languages/csharpImports.js.map +1 -0
- package/dist/core/languages/goFunctions.js +31 -7
- package/dist/core/languages/goFunctions.js.map +1 -1
- package/dist/core/languages/javaFunctions.js +12 -5
- package/dist/core/languages/javaFunctions.js.map +1 -1
- package/dist/core/languages/phpAdapter.d.ts +2 -0
- package/dist/core/languages/phpAdapter.js +149 -0
- package/dist/core/languages/phpAdapter.js.map +1 -0
- package/dist/core/languages/phpCallSites.d.ts +21 -0
- package/dist/core/languages/phpCallSites.js +56 -0
- package/dist/core/languages/phpCallSites.js.map +1 -0
- package/dist/core/languages/phpCyclomatic.d.ts +27 -0
- package/dist/core/languages/phpCyclomatic.js +69 -0
- package/dist/core/languages/phpCyclomatic.js.map +1 -0
- package/dist/core/languages/phpExports.d.ts +13 -0
- package/dist/core/languages/phpExports.js +58 -0
- package/dist/core/languages/phpExports.js.map +1 -0
- package/dist/core/languages/phpFunctions.d.ts +25 -0
- package/dist/core/languages/phpFunctions.js +144 -0
- package/dist/core/languages/phpFunctions.js.map +1 -0
- package/dist/core/languages/phpImports.d.ts +31 -0
- package/dist/core/languages/phpImports.js +142 -0
- package/dist/core/languages/phpImports.js.map +1 -0
- package/dist/core/languages/phpManifests.d.ts +31 -0
- package/dist/core/languages/phpManifests.js +89 -0
- package/dist/core/languages/phpManifests.js.map +1 -0
- package/dist/core/languages/pythonFunctions.js +34 -8
- package/dist/core/languages/pythonFunctions.js.map +1 -1
- package/dist/core/languages/registry.js +3 -1
- package/dist/core/languages/registry.js.map +1 -1
- package/dist/core/languages/rubyFunctions.js +37 -5
- package/dist/core/languages/rubyFunctions.js.map +1 -1
- package/dist/core/languages/rustFunctions.js +44 -8
- package/dist/core/languages/rustFunctions.js.map +1 -1
- package/dist/core/languages/treeSitterLoader.js +3 -1
- package/dist/core/languages/treeSitterLoader.js.map +1 -1
- package/dist/grammars/tree-sitter-c_sharp.wasm +0 -0
- package/dist/grammars/tree-sitter-php.wasm +0 -0
- package/dist/reporters/htmlReporter.d.ts +3 -1
- package/dist/reporters/htmlReporter.js +86 -0
- package/dist/reporters/htmlReporter.js.map +1 -1
- package/dist/tool-manifest.json +2 -2
- package/package.json +4 -2
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract called-function names from C#. We surface a deduplicated list of
|
|
3
|
+
* bare identifiers (mirrors JS / Python / Go / Java / Ruby / Rust / PHP).
|
|
4
|
+
*
|
|
5
|
+
* invocation_expression : function child is one of:
|
|
6
|
+
* identifier foo(args) → "foo"
|
|
7
|
+
* member_access_expression obj.Method(args) → "Method"
|
|
8
|
+
* generic_name Foo<T>(args) → "Foo"
|
|
9
|
+
* conditional_access_expression obj?.Method(args) → "Method"
|
|
10
|
+
*
|
|
11
|
+
* Object-creation calls (`new Foo()`) are NOT surfaced — they're tracked as
|
|
12
|
+
* type instantiations elsewhere if needed; consistent with the other adapters
|
|
13
|
+
* which don't emit constructors as call sites.
|
|
14
|
+
*/
|
|
15
|
+
export function extractCsharpCallSites(root) {
|
|
16
|
+
const seen = new Set();
|
|
17
|
+
walk(root, (n) => {
|
|
18
|
+
if (n.type !== 'invocation_expression')
|
|
19
|
+
return;
|
|
20
|
+
const fn = n.childForFieldName ? n.childForFieldName('function') : firstNamedChild(n);
|
|
21
|
+
if (!fn)
|
|
22
|
+
return;
|
|
23
|
+
const name = nameOf(fn);
|
|
24
|
+
if (name)
|
|
25
|
+
seen.add(name);
|
|
26
|
+
});
|
|
27
|
+
return [...seen];
|
|
28
|
+
}
|
|
29
|
+
function nameOf(node) {
|
|
30
|
+
switch (node.type) {
|
|
31
|
+
case 'identifier':
|
|
32
|
+
return node.text;
|
|
33
|
+
case 'member_access_expression':
|
|
34
|
+
case 'conditional_access_expression':
|
|
35
|
+
case 'member_binding_expression': {
|
|
36
|
+
const name = node.childForFieldName ? node.childForFieldName('name') : null;
|
|
37
|
+
if (name)
|
|
38
|
+
return nameOf(name);
|
|
39
|
+
// Fallback: last identifier-bearing child.
|
|
40
|
+
const named = node.namedChildren;
|
|
41
|
+
if (named.length === 0)
|
|
42
|
+
return null;
|
|
43
|
+
return nameOf(named[named.length - 1]);
|
|
44
|
+
}
|
|
45
|
+
case 'generic_name': {
|
|
46
|
+
for (const c of node.namedChildren) {
|
|
47
|
+
if (c.type === 'identifier')
|
|
48
|
+
return c.text;
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
case 'qualified_name': {
|
|
53
|
+
// Take the last segment.
|
|
54
|
+
let last = null;
|
|
55
|
+
for (const c of node.namedChildren) {
|
|
56
|
+
if (c.type === 'identifier')
|
|
57
|
+
last = c.text;
|
|
58
|
+
}
|
|
59
|
+
return last;
|
|
60
|
+
}
|
|
61
|
+
default:
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function firstNamedChild(n) {
|
|
66
|
+
return n.namedChildren[0] ?? null;
|
|
67
|
+
}
|
|
68
|
+
function walk(node, visit) {
|
|
69
|
+
visit(node);
|
|
70
|
+
for (const child of node.namedChildren)
|
|
71
|
+
walk(child, visit);
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=csharpCallSites.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csharpCallSites.js","sourceRoot":"","sources":["../../../src/core/languages/csharpCallSites.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE;QACf,IAAI,CAAC,CAAC,IAAI,KAAK,uBAAuB;YAAE,OAAO;QAC/C,MAAM,EAAE,GAAG,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QACtF,IAAI,CAAC,EAAE;YAAE,OAAO;QAChB,MAAM,IAAI,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC;QACxB,IAAI,IAAI;YAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;AACnB,CAAC;AAED,SAAS,MAAM,CAAC,IAAY;IAC1B,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,YAAY;YACf,OAAO,IAAI,CAAC,IAAI,CAAC;QACnB,KAAK,0BAA0B,CAAC;QAChC,KAAK,+BAA+B,CAAC;QACrC,KAAK,2BAA2B,CAAC,CAAC,CAAC;YACjC,MAAM,IAAI,GAAG,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAC5E,IAAI,IAAI;gBAAE,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC;YAC9B,2CAA2C;YAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC;YACjC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC;YACpC,OAAO,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;QACzC,CAAC;QACD,KAAK,cAAc,CAAC,CAAC,CAAC;YACpB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;gBACnC,IAAI,CAAC,CAAC,IAAI,KAAK,YAAY;oBAAE,OAAO,CAAC,CAAC,IAAI,CAAC;YAC7C,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,KAAK,gBAAgB,CAAC,CAAC,CAAC;YACtB,yBAAyB;YACzB,IAAI,IAAI,GAAkB,IAAI,CAAC;YAC/B,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;gBACnC,IAAI,CAAC,CAAC,IAAI,KAAK,YAAY;oBAAE,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC;YAC7C,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QACD;YACE,OAAO,IAAI,CAAC;IAChB,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CAAC,CAAS;IAChC,OAAO,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AACpC,CAAC;AAED,SAAS,IAAI,CAAC,IAAY,EAAE,KAA0B;IACpD,KAAK,CAAC,IAAI,CAAC,CAAC;IACZ,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,aAAa;QAAE,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAC7D,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
interface TsNode {
|
|
2
|
+
type: string;
|
|
3
|
+
text: string;
|
|
4
|
+
namedChildren: TsNode[];
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* File-level McCabe cyclomatic complexity for tree-sitter-c-sharp.
|
|
8
|
+
*
|
|
9
|
+
* Decision points:
|
|
10
|
+
* if_statement +1
|
|
11
|
+
* for_statement / foreach_statement +1 each
|
|
12
|
+
* while_statement / do_statement +1 each
|
|
13
|
+
* switch_section +1 per non-default arm
|
|
14
|
+
* switch_expression_arm +1 per non-discard arm
|
|
15
|
+
* catch_clause +1 each
|
|
16
|
+
* conditional_expression (ternary `?:`) +1
|
|
17
|
+
* binary_expression with `&&` / `||` / `??` +1 each
|
|
18
|
+
*
|
|
19
|
+
* `else if` is modeled by the grammar as `else_clause > if_statement`, so
|
|
20
|
+
* the inner if_statement counts naturally.
|
|
21
|
+
*/
|
|
22
|
+
export declare function extractCsharpCyclomatic(root: TsNode): number;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-level McCabe cyclomatic complexity for tree-sitter-c-sharp.
|
|
3
|
+
*
|
|
4
|
+
* Decision points:
|
|
5
|
+
* if_statement +1
|
|
6
|
+
* for_statement / foreach_statement +1 each
|
|
7
|
+
* while_statement / do_statement +1 each
|
|
8
|
+
* switch_section +1 per non-default arm
|
|
9
|
+
* switch_expression_arm +1 per non-discard arm
|
|
10
|
+
* catch_clause +1 each
|
|
11
|
+
* conditional_expression (ternary `?:`) +1
|
|
12
|
+
* binary_expression with `&&` / `||` / `??` +1 each
|
|
13
|
+
*
|
|
14
|
+
* `else if` is modeled by the grammar as `else_clause > if_statement`, so
|
|
15
|
+
* the inner if_statement counts naturally.
|
|
16
|
+
*/
|
|
17
|
+
export function extractCsharpCyclomatic(root) {
|
|
18
|
+
let decisions = 0;
|
|
19
|
+
walk(root, (n) => {
|
|
20
|
+
if (isDecisionPoint(n))
|
|
21
|
+
decisions++;
|
|
22
|
+
});
|
|
23
|
+
return decisions + 1;
|
|
24
|
+
}
|
|
25
|
+
function isDecisionPoint(n) {
|
|
26
|
+
switch (n.type) {
|
|
27
|
+
case 'if_statement':
|
|
28
|
+
case 'for_statement':
|
|
29
|
+
case 'foreach_statement':
|
|
30
|
+
case 'while_statement':
|
|
31
|
+
case 'do_statement':
|
|
32
|
+
case 'catch_clause':
|
|
33
|
+
case 'conditional_expression':
|
|
34
|
+
return true;
|
|
35
|
+
case 'switch_section': {
|
|
36
|
+
// A switch_section starts with `case X:` or `default:`. The default
|
|
37
|
+
// arm is the fallthrough; everything else is a branch. Note: each
|
|
38
|
+
// case label gets its own switch_section even with `case 1: case 2:`.
|
|
39
|
+
return !/^\s*default\b/.test(n.text);
|
|
40
|
+
}
|
|
41
|
+
case 'switch_expression_arm': {
|
|
42
|
+
// `_ =>` (discard) is the fallthrough; everything else is a branch.
|
|
43
|
+
const t = n.text.trimStart();
|
|
44
|
+
return !/^_\s*=>/.test(t);
|
|
45
|
+
}
|
|
46
|
+
case 'binary_expression': {
|
|
47
|
+
const t = n.text;
|
|
48
|
+
if (/(\s|^)(\|\||&&|\?\?)(\s|$)/.test(t))
|
|
49
|
+
return true;
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
default:
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function walk(node, visit) {
|
|
57
|
+
visit(node);
|
|
58
|
+
for (const child of node.namedChildren)
|
|
59
|
+
walk(child, visit);
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=csharpCyclomatic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csharpCyclomatic.js","sourceRoot":"","sources":["../../../src/core/languages/csharpCyclomatic.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAAY;IAClD,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE;QACf,IAAI,eAAe,CAAC,CAAC,CAAC;YAAE,SAAS,EAAE,CAAC;IACtC,CAAC,CAAC,CAAC;IACH,OAAO,SAAS,GAAG,CAAC,CAAC;AACvB,CAAC;AAED,SAAS,eAAe,CAAC,CAAS;IAChC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;QACf,KAAK,cAAc,CAAC;QACpB,KAAK,eAAe,CAAC;QACrB,KAAK,mBAAmB,CAAC;QACzB,KAAK,iBAAiB,CAAC;QACvB,KAAK,cAAc,CAAC;QACpB,KAAK,cAAc,CAAC;QACpB,KAAK,wBAAwB;YAC3B,OAAO,IAAI,CAAC;QACd,KAAK,gBAAgB,CAAC,CAAC,CAAC;YACtB,oEAAoE;YACpE,kEAAkE;YAClE,sEAAsE;YACtE,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACvC,CAAC;QACD,KAAK,uBAAuB,CAAC,CAAC,CAAC;YAC7B,oEAAoE;YACpE,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YAC7B,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC5B,CAAC;QACD,KAAK,mBAAmB,CAAC,CAAC,CAAC;YACzB,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YACjB,IAAI,4BAA4B,CAAC,IAAI,CAAC,CAAC,CAAC;gBAAE,OAAO,IAAI,CAAC;YACtD,OAAO,KAAK,CAAC;QACf,CAAC;QACD;YACE,OAAO,KAAK,CAAC;IACjB,CAAC;AACH,CAAC;AAED,SAAS,IAAI,CAAC,IAAY,EAAE,KAA0B;IACpD,KAAK,CAAC,IAAI,CAAC,CAAC;IACZ,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,aAAa;QAAE,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAC7D,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AstExport } from '../ast.js';
|
|
2
|
+
interface TsNode {
|
|
3
|
+
type: string;
|
|
4
|
+
text: string;
|
|
5
|
+
startPosition: {
|
|
6
|
+
row: number;
|
|
7
|
+
column: number;
|
|
8
|
+
};
|
|
9
|
+
namedChildren: TsNode[];
|
|
10
|
+
childForFieldName?: (name: string) => TsNode | null;
|
|
11
|
+
}
|
|
12
|
+
export declare function extractCsharpExports(root: TsNode): AstExport[];
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* C# top-level type declarations are visible to anything that imports their
|
|
3
|
+
* namespace iff they are `public`. Everything else (`internal`, `private`,
|
|
4
|
+
* `file`, no modifier — defaults to `internal` at namespace scope) stays
|
|
5
|
+
* inside the assembly and is *not* an export from this file.
|
|
6
|
+
*
|
|
7
|
+
* Type kinds we surface:
|
|
8
|
+
* class_declaration / record_declaration / struct_declaration → 'class'
|
|
9
|
+
* interface_declaration → 'interface'
|
|
10
|
+
* enum_declaration → 'enum'
|
|
11
|
+
* delegate_declaration → 'function'
|
|
12
|
+
* method_declaration (top-level statements / static class) → 'function'
|
|
13
|
+
*
|
|
14
|
+
* We descend into namespace_declaration / file_scoped_namespace_declaration
|
|
15
|
+
* so namespace-wrapped types still count as top-level for export purposes.
|
|
16
|
+
*/
|
|
17
|
+
const TOP_LEVEL_KINDS = {
|
|
18
|
+
class_declaration: 'class',
|
|
19
|
+
record_declaration: 'class',
|
|
20
|
+
struct_declaration: 'class',
|
|
21
|
+
interface_declaration: 'interface',
|
|
22
|
+
enum_declaration: 'enum',
|
|
23
|
+
delegate_declaration: 'function',
|
|
24
|
+
method_declaration: 'function',
|
|
25
|
+
};
|
|
26
|
+
export function extractCsharpExports(root) {
|
|
27
|
+
const exports = [];
|
|
28
|
+
for (const child of root.namedChildren)
|
|
29
|
+
visit(child, exports);
|
|
30
|
+
return exports;
|
|
31
|
+
}
|
|
32
|
+
function visit(node, out) {
|
|
33
|
+
if (node.type === 'namespace_declaration' || node.type === 'file_scoped_namespace_declaration') {
|
|
34
|
+
const body = node.namedChildren.find((c) => c.type === 'declaration_list');
|
|
35
|
+
const target = body ?? node;
|
|
36
|
+
for (const c of target.namedChildren)
|
|
37
|
+
visit(c, out);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const kind = TOP_LEVEL_KINDS[node.type];
|
|
41
|
+
if (!kind)
|
|
42
|
+
return;
|
|
43
|
+
if (!isPublic(node))
|
|
44
|
+
return;
|
|
45
|
+
const name = nameOf(node);
|
|
46
|
+
if (!name)
|
|
47
|
+
return;
|
|
48
|
+
out.push({ name, kind, typeOnly: false, line: node.startPosition.row + 1 });
|
|
49
|
+
}
|
|
50
|
+
function isPublic(node) {
|
|
51
|
+
for (const c of node.namedChildren) {
|
|
52
|
+
if (c.type === 'modifier' && c.text === 'public')
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
function nameOf(node) {
|
|
58
|
+
if (node.childForFieldName) {
|
|
59
|
+
const id = node.childForFieldName('name');
|
|
60
|
+
if (id)
|
|
61
|
+
return id.text;
|
|
62
|
+
}
|
|
63
|
+
for (const c of node.namedChildren) {
|
|
64
|
+
if (c.type === 'identifier')
|
|
65
|
+
return c.text;
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=csharpExports.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csharpExports.js","sourceRoot":"","sources":["../../../src/core/languages/csharpExports.ts"],"names":[],"mappings":"AAUA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,eAAe,GAA+B;IAClD,iBAAiB,EAAE,OAAO;IAC1B,kBAAkB,EAAE,OAAO;IAC3B,kBAAkB,EAAE,OAAO;IAC3B,qBAAqB,EAAE,WAAW;IAClC,gBAAgB,EAAE,MAAM;IACxB,oBAAoB,EAAE,UAAU;IAChC,kBAAkB,EAAE,UAAU;CAC/B,CAAC;AAEF,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,MAAM,OAAO,GAAgB,EAAE,CAAC;IAChC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,aAAa;QAAE,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC9D,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,KAAK,CAAC,IAAY,EAAE,GAAgB;IAC3C,IAAI,IAAI,CAAC,IAAI,KAAK,uBAAuB,IAAI,IAAI,CAAC,IAAI,KAAK,mCAAmC,EAAE,CAAC;QAC/F,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,kBAAkB,CAAC,CAAC;QAC3E,MAAM,MAAM,GAAG,IAAI,IAAI,IAAI,CAAC;QAC5B,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,aAAa;YAAE,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QACpD,OAAO;IACT,CAAC;IACD,MAAM,IAAI,GAAG,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI;QAAE,OAAO;IAClB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO;IAC5B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1B,IAAI,CAAC,IAAI;QAAE,OAAO;IAClB,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,aAAa,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC;AAC9E,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY;IAC5B,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;QACnC,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;IAChE,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,MAAM,CAAC,IAAY;IAC1B,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC3B,MAAM,EAAE,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;QAC1C,IAAI,EAAE;YAAE,OAAO,EAAE,CAAC,IAAI,CAAC;IACzB,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;QACnC,IAAI,CAAC,CAAC,IAAI,KAAK,YAAY;YAAE,OAAO,CAAC,CAAC,IAAI,CAAC;IAC7C,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { FunctionInfo } from '../ast.js';
|
|
2
|
+
interface TsNode {
|
|
3
|
+
type: string;
|
|
4
|
+
text: string;
|
|
5
|
+
startPosition: {
|
|
6
|
+
row: number;
|
|
7
|
+
column: number;
|
|
8
|
+
};
|
|
9
|
+
endPosition: {
|
|
10
|
+
row: number;
|
|
11
|
+
column: number;
|
|
12
|
+
};
|
|
13
|
+
namedChildren: TsNode[];
|
|
14
|
+
childForFieldName?: (name: string) => TsNode | null;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Per-function McCabe CC for C#. Walks `method_declaration`,
|
|
18
|
+
* `constructor_declaration`, `local_function_statement`, and
|
|
19
|
+
* `destructor_declaration` nodes. Methods inside a type are named
|
|
20
|
+
* `Type.method`. Lambdas (parenthesized_lambda_expression / lambda_expression)
|
|
21
|
+
* fold into their enclosing function.
|
|
22
|
+
*/
|
|
23
|
+
export declare function extractCsharpFunctions(root: TsNode): FunctionInfo[];
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
const CSHARP_DECISION_NODES = new Set([
|
|
2
|
+
'if_statement',
|
|
3
|
+
'for_statement',
|
|
4
|
+
'foreach_statement',
|
|
5
|
+
'while_statement',
|
|
6
|
+
'do_statement',
|
|
7
|
+
'catch_clause',
|
|
8
|
+
'conditional_expression',
|
|
9
|
+
]);
|
|
10
|
+
/**
|
|
11
|
+
* Per-function McCabe CC for C#. Walks `method_declaration`,
|
|
12
|
+
* `constructor_declaration`, `local_function_statement`, and
|
|
13
|
+
* `destructor_declaration` nodes. Methods inside a type are named
|
|
14
|
+
* `Type.method`. Lambdas (parenthesized_lambda_expression / lambda_expression)
|
|
15
|
+
* fold into their enclosing function.
|
|
16
|
+
*/
|
|
17
|
+
export function extractCsharpFunctions(root) {
|
|
18
|
+
const out = [];
|
|
19
|
+
walk(root, null, out);
|
|
20
|
+
return out;
|
|
21
|
+
}
|
|
22
|
+
function walk(node, ownerName, out) {
|
|
23
|
+
if (node.type === 'class_declaration' ||
|
|
24
|
+
node.type === 'struct_declaration' ||
|
|
25
|
+
node.type === 'interface_declaration' ||
|
|
26
|
+
node.type === 'record_declaration' ||
|
|
27
|
+
node.type === 'enum_declaration') {
|
|
28
|
+
const cls = nameOfType(node) ?? ownerName;
|
|
29
|
+
const body = node.namedChildren.find((c) => c.type === 'declaration_list');
|
|
30
|
+
if (body) {
|
|
31
|
+
for (const child of body.namedChildren)
|
|
32
|
+
walk(child, cls, out);
|
|
33
|
+
}
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (node.type === 'namespace_declaration' ||
|
|
37
|
+
node.type === 'file_scoped_namespace_declaration' ||
|
|
38
|
+
node.type === 'compilation_unit' ||
|
|
39
|
+
node.type === 'declaration_list') {
|
|
40
|
+
for (const child of node.namedChildren)
|
|
41
|
+
walk(child, ownerName, out);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (node.type === 'method_declaration' ||
|
|
45
|
+
node.type === 'constructor_declaration' ||
|
|
46
|
+
node.type === 'destructor_declaration' ||
|
|
47
|
+
node.type === 'local_function_statement') {
|
|
48
|
+
const baseName = nameOfFn(node) ?? '<anonymous>';
|
|
49
|
+
const fnName = ownerName ? `${ownerName}.${baseName}` : baseName;
|
|
50
|
+
const line = node.startPosition.row + 1;
|
|
51
|
+
const endLine = node.endPosition.row + 1;
|
|
52
|
+
const { cc, callSites } = analyzeBody(node);
|
|
53
|
+
out.push({ name: fnName, line, endLine, cyclomaticComplexity: cc, callSites });
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
for (const child of node.namedChildren)
|
|
57
|
+
walk(child, ownerName, out);
|
|
58
|
+
}
|
|
59
|
+
function analyzeBody(fnNode) {
|
|
60
|
+
let count = 0;
|
|
61
|
+
const calls = new Set();
|
|
62
|
+
const body = fnNode.namedChildren.find((c) => c.type === 'block') ??
|
|
63
|
+
fnNode.namedChildren.find((c) => c.type === 'arrow_expression_clause');
|
|
64
|
+
if (!body)
|
|
65
|
+
return { cc: 1, callSites: [] };
|
|
66
|
+
walkSkipNested(body, (n) => {
|
|
67
|
+
if (CSHARP_DECISION_NODES.has(n.type)) {
|
|
68
|
+
count++;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (n.type === 'switch_section') {
|
|
72
|
+
if (!/^\s*default\b/.test(n.text))
|
|
73
|
+
count++;
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (n.type === 'switch_expression_arm') {
|
|
77
|
+
const t = n.text.trimStart();
|
|
78
|
+
if (!/^_\s*=>/.test(t))
|
|
79
|
+
count++;
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (n.type === 'binary_expression') {
|
|
83
|
+
if (/(\s|^)(\|\||&&|\?\?)(\s|$)/.test(n.text))
|
|
84
|
+
count++;
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (n.type === 'invocation_expression') {
|
|
88
|
+
const name = csharpCalleeName(n);
|
|
89
|
+
if (name)
|
|
90
|
+
calls.add(name);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
return { cc: count + 1, callSites: [...calls] };
|
|
94
|
+
}
|
|
95
|
+
function csharpCalleeName(call) {
|
|
96
|
+
const fn = call.childForFieldName ? call.childForFieldName('function') : (call.namedChildren[0] ?? null);
|
|
97
|
+
if (!fn)
|
|
98
|
+
return null;
|
|
99
|
+
return calleeBareName(fn);
|
|
100
|
+
}
|
|
101
|
+
function calleeBareName(node) {
|
|
102
|
+
switch (node.type) {
|
|
103
|
+
case 'identifier':
|
|
104
|
+
return node.text;
|
|
105
|
+
case 'member_access_expression':
|
|
106
|
+
case 'conditional_access_expression':
|
|
107
|
+
case 'member_binding_expression': {
|
|
108
|
+
const name = node.childForFieldName ? node.childForFieldName('name') : null;
|
|
109
|
+
if (name)
|
|
110
|
+
return calleeBareName(name);
|
|
111
|
+
const named = node.namedChildren;
|
|
112
|
+
return named.length > 0 ? calleeBareName(named[named.length - 1]) : null;
|
|
113
|
+
}
|
|
114
|
+
case 'generic_name': {
|
|
115
|
+
for (const c of node.namedChildren) {
|
|
116
|
+
if (c.type === 'identifier')
|
|
117
|
+
return c.text;
|
|
118
|
+
}
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
case 'qualified_name': {
|
|
122
|
+
let last = null;
|
|
123
|
+
for (const c of node.namedChildren) {
|
|
124
|
+
if (c.type === 'identifier')
|
|
125
|
+
last = c.text;
|
|
126
|
+
}
|
|
127
|
+
return last;
|
|
128
|
+
}
|
|
129
|
+
default:
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function walkSkipNested(node, visit) {
|
|
134
|
+
visit(node);
|
|
135
|
+
for (const child of node.namedChildren) {
|
|
136
|
+
if (child.type === 'method_declaration' ||
|
|
137
|
+
child.type === 'constructor_declaration' ||
|
|
138
|
+
child.type === 'local_function_statement' ||
|
|
139
|
+
child.type === 'lambda_expression' ||
|
|
140
|
+
child.type === 'parenthesized_lambda_expression' ||
|
|
141
|
+
child.type === 'anonymous_method_expression') {
|
|
142
|
+
// Nested fn / lambda: skip so its decisions don't double-count.
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
walkSkipNested(child, visit);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function nameOfType(node) {
|
|
149
|
+
for (const c of node.namedChildren) {
|
|
150
|
+
if (c.type === 'identifier')
|
|
151
|
+
return c.text;
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
function nameOfFn(node) {
|
|
156
|
+
if (node.childForFieldName) {
|
|
157
|
+
const id = node.childForFieldName('name');
|
|
158
|
+
if (id)
|
|
159
|
+
return id.text;
|
|
160
|
+
}
|
|
161
|
+
// For methods, the name is the identifier that comes AFTER the return type.
|
|
162
|
+
// For constructors / destructors, it's the only identifier child.
|
|
163
|
+
let lastIdentifier = null;
|
|
164
|
+
for (const c of node.namedChildren) {
|
|
165
|
+
if (c.type === 'identifier')
|
|
166
|
+
lastIdentifier = c.text;
|
|
167
|
+
}
|
|
168
|
+
return lastIdentifier;
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=csharpFunctions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csharpFunctions.js","sourceRoot":"","sources":["../../../src/core/languages/csharpFunctions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { AstImport } from '../ast.js';
|
|
2
|
+
interface TsNode {
|
|
3
|
+
type: string;
|
|
4
|
+
text: string;
|
|
5
|
+
startPosition: {
|
|
6
|
+
row: number;
|
|
7
|
+
column: number;
|
|
8
|
+
};
|
|
9
|
+
namedChildren: TsNode[];
|
|
10
|
+
childForFieldName?: (name: string) => TsNode | null;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Extract C# `using` directives from a tree-sitter-c-sharp AST.
|
|
14
|
+
*
|
|
15
|
+
* Handled forms:
|
|
16
|
+
* using System; → "System"
|
|
17
|
+
* using System.Collections.Generic; → "System.Collections.Generic"
|
|
18
|
+
* using static System.Console; → "System.Console"
|
|
19
|
+
* using Alias = System.Collections.Generic; → "System.Collections.Generic", alias "Alias"
|
|
20
|
+
*
|
|
21
|
+
* We surface the full dotted namespace (no `using static` distinction at the
|
|
22
|
+
* AstImport level — same as Java's `import static foo.Bar.method;`).
|
|
23
|
+
*
|
|
24
|
+
* NOT handled:
|
|
25
|
+
* - Global usings (file-scoped namespaces) — same shape, surfaces normally.
|
|
26
|
+
* - Aliased open generic types (`using L = List<int>`) — alias is preserved
|
|
27
|
+
* but the path is the unqualified type form (still useful for fan-out).
|
|
28
|
+
*/
|
|
29
|
+
export declare function extractCsharpImports(root: TsNode): AstImport[];
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract C# `using` directives from a tree-sitter-c-sharp AST.
|
|
3
|
+
*
|
|
4
|
+
* Handled forms:
|
|
5
|
+
* using System; → "System"
|
|
6
|
+
* using System.Collections.Generic; → "System.Collections.Generic"
|
|
7
|
+
* using static System.Console; → "System.Console"
|
|
8
|
+
* using Alias = System.Collections.Generic; → "System.Collections.Generic", alias "Alias"
|
|
9
|
+
*
|
|
10
|
+
* We surface the full dotted namespace (no `using static` distinction at the
|
|
11
|
+
* AstImport level — same as Java's `import static foo.Bar.method;`).
|
|
12
|
+
*
|
|
13
|
+
* NOT handled:
|
|
14
|
+
* - Global usings (file-scoped namespaces) — same shape, surfaces normally.
|
|
15
|
+
* - Aliased open generic types (`using L = List<int>`) — alias is preserved
|
|
16
|
+
* but the path is the unqualified type form (still useful for fan-out).
|
|
17
|
+
*/
|
|
18
|
+
export function extractCsharpImports(root) {
|
|
19
|
+
const imports = [];
|
|
20
|
+
for (const child of root.namedChildren) {
|
|
21
|
+
if (child.type === 'using_directive')
|
|
22
|
+
collectUsing(child, imports);
|
|
23
|
+
else if (child.type === 'file_scoped_namespace_declaration' || child.type === 'namespace_declaration') {
|
|
24
|
+
// Inside a namespace, more usings can appear.
|
|
25
|
+
for (const inner of child.namedChildren) {
|
|
26
|
+
if (inner.type === 'using_directive')
|
|
27
|
+
collectUsing(inner, imports);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return imports;
|
|
32
|
+
}
|
|
33
|
+
function collectUsing(node, out) {
|
|
34
|
+
const line = node.startPosition.row + 1;
|
|
35
|
+
// Aliased: first identifier is the alias, second is the path.
|
|
36
|
+
// Plain: first child is the path (identifier or qualified_name).
|
|
37
|
+
// Static: the keyword `static` is anonymous; the path is the only named child.
|
|
38
|
+
const named = node.namedChildren;
|
|
39
|
+
if (named.length === 0)
|
|
40
|
+
return;
|
|
41
|
+
let alias;
|
|
42
|
+
let pathNode = null;
|
|
43
|
+
if (named.length >= 2 && named[0].type === 'identifier' && (named[1].type === 'qualified_name' || named[1].type === 'identifier' || named[1].type === 'generic_name')) {
|
|
44
|
+
alias = named[0].text;
|
|
45
|
+
pathNode = named[1];
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
// Last named child is the path (skip any keyword-like tokens that the
|
|
49
|
+
// grammar may surface as a name).
|
|
50
|
+
pathNode = named[named.length - 1];
|
|
51
|
+
}
|
|
52
|
+
if (!pathNode)
|
|
53
|
+
return;
|
|
54
|
+
const source = readPath(pathNode);
|
|
55
|
+
if (!source)
|
|
56
|
+
return;
|
|
57
|
+
out.push({
|
|
58
|
+
source,
|
|
59
|
+
kind: 'static',
|
|
60
|
+
specifiers: alias ? [alias] : [],
|
|
61
|
+
typeOnly: false,
|
|
62
|
+
line,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
function readPath(node) {
|
|
66
|
+
if (node.type === 'identifier')
|
|
67
|
+
return node.text;
|
|
68
|
+
if (node.type === 'qualified_name') {
|
|
69
|
+
const parts = [];
|
|
70
|
+
collect(node, parts);
|
|
71
|
+
return parts.join('.');
|
|
72
|
+
}
|
|
73
|
+
if (node.type === 'generic_name') {
|
|
74
|
+
// For `using X = List<int>` style — drop the type arguments.
|
|
75
|
+
for (const c of node.namedChildren) {
|
|
76
|
+
if (c.type === 'identifier')
|
|
77
|
+
return c.text;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return node.text;
|
|
81
|
+
}
|
|
82
|
+
function collect(node, out) {
|
|
83
|
+
if (node.type === 'identifier') {
|
|
84
|
+
out.push(node.text);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
for (const c of node.namedChildren)
|
|
88
|
+
collect(c, out);
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=csharpImports.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csharpImports.js","sourceRoot":"","sources":["../../../src/core/languages/csharpImports.ts"],"names":[],"mappings":"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"}
|
|
@@ -22,10 +22,8 @@ function walk(node, out) {
|
|
|
22
22
|
const fnName = nameOfGoFunction(node);
|
|
23
23
|
const line = node.startPosition.row + 1;
|
|
24
24
|
const endLine = node.endPosition.row + 1;
|
|
25
|
-
const cc =
|
|
26
|
-
out.push({ name: fnName, line, endLine, cyclomaticComplexity: cc });
|
|
27
|
-
// Don't recurse into the body for FunctionInfo - we don't track func
|
|
28
|
-
// literals separately in 0.13.0 (they're not addressable by name anyway).
|
|
25
|
+
const { cc, callSites } = analyzeBody(node);
|
|
26
|
+
out.push({ name: fnName, line, endLine, cyclomaticComplexity: cc, callSites });
|
|
29
27
|
return;
|
|
30
28
|
}
|
|
31
29
|
for (const child of node.namedChildren)
|
|
@@ -59,11 +57,12 @@ function extractReceiverType(text) {
|
|
|
59
57
|
const m = /(?:\*)?([A-Za-z_][A-Za-z0-9_]*)(?:\[[^\]]*\])?\s*$/.exec(inner);
|
|
60
58
|
return m ? m[1] : null;
|
|
61
59
|
}
|
|
62
|
-
function
|
|
60
|
+
function analyzeBody(fnNode) {
|
|
63
61
|
let count = 0;
|
|
62
|
+
const calls = new Set();
|
|
64
63
|
const body = fnNode.childForFieldName ? fnNode.childForFieldName('body') : null;
|
|
65
64
|
if (!body)
|
|
66
|
-
return 1;
|
|
65
|
+
return { cc: 1, callSites: [] };
|
|
67
66
|
walkSkipNested(body, (n) => {
|
|
68
67
|
if (GO_DECISION_NODES.has(n.type)) {
|
|
69
68
|
count++;
|
|
@@ -72,9 +71,34 @@ function countDecisions(fnNode) {
|
|
|
72
71
|
if (n.type === 'binary_expression') {
|
|
73
72
|
if (/(\s|^)(\|\||&&)(\s|$)/.test(n.text))
|
|
74
73
|
count++;
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (n.type === 'call_expression') {
|
|
77
|
+
const fn = n.childForFieldName ? n.childForFieldName('function') : (n.namedChildren[0] ?? null);
|
|
78
|
+
const name = goCalleeName(fn);
|
|
79
|
+
if (name)
|
|
80
|
+
calls.add(name);
|
|
75
81
|
}
|
|
76
82
|
});
|
|
77
|
-
return count + 1;
|
|
83
|
+
return { cc: count + 1, callSites: [...calls] };
|
|
84
|
+
}
|
|
85
|
+
function goCalleeName(node) {
|
|
86
|
+
if (!node)
|
|
87
|
+
return null;
|
|
88
|
+
switch (node.type) {
|
|
89
|
+
case 'identifier':
|
|
90
|
+
case 'field_identifier':
|
|
91
|
+
return node.text;
|
|
92
|
+
case 'selector_expression': {
|
|
93
|
+
const field = node.childForFieldName ? node.childForFieldName('field') : null;
|
|
94
|
+
if (field)
|
|
95
|
+
return goCalleeName(field);
|
|
96
|
+
const named = node.namedChildren;
|
|
97
|
+
return named.length > 0 ? goCalleeName(named[named.length - 1]) : null;
|
|
98
|
+
}
|
|
99
|
+
default:
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
78
102
|
}
|
|
79
103
|
function walkSkipNested(node, visit) {
|
|
80
104
|
visit(node);
|