@liendev/parser 0.46.0 → 0.48.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 +4 -2
- package/dist/ast/languages/kotlin.d.ts +83 -0
- package/dist/ast/languages/kotlin.d.ts.map +1 -0
- package/dist/ast/languages/kotlin.js +506 -0
- package/dist/ast/languages/kotlin.js.map +1 -0
- package/dist/ast/languages/registry.d.ts +1 -1
- package/dist/ast/languages/registry.d.ts.map +1 -1
- package/dist/ast/languages/registry.js +6 -0
- package/dist/ast/languages/registry.js.map +1 -1
- package/dist/ast/languages/swift.d.ts +98 -0
- package/dist/ast/languages/swift.d.ts.map +1 -0
- package/dist/ast/languages/swift.js +529 -0
- package/dist/ast/languages/swift.js.map +1 -0
- package/dist/ast/symbols.d.ts.map +1 -1
- package/dist/ast/symbols.js +28 -7
- package/dist/ast/symbols.js.map +1 -1
- package/dist/symbol-extractor.d.ts.map +1 -1
- package/dist/symbol-extractor.js +87 -0
- package/dist/symbol-extractor.js.map +1 -1
- package/dist/utils/path-matching.d.ts +4 -0
- package/dist/utils/path-matching.d.ts.map +1 -1
- package/dist/utils/path-matching.js +7 -1
- package/dist/utils/path-matching.js.map +1 -1
- package/package.json +3 -1
|
@@ -8,6 +8,8 @@ import { goDefinition } from './go.js';
|
|
|
8
8
|
import { javaDefinition } from './java.js';
|
|
9
9
|
import { csharpDefinition } from './csharp.js';
|
|
10
10
|
import { rubyDefinition } from './ruby.js';
|
|
11
|
+
import { kotlinDefinition } from './kotlin.js';
|
|
12
|
+
import { swiftDefinition } from './swift.js';
|
|
11
13
|
/**
|
|
12
14
|
* All registered language definitions.
|
|
13
15
|
* To add a new language, create a definition file and add it here.
|
|
@@ -22,6 +24,8 @@ const definitions = [
|
|
|
22
24
|
javaDefinition,
|
|
23
25
|
csharpDefinition,
|
|
24
26
|
rubyDefinition,
|
|
27
|
+
kotlinDefinition,
|
|
28
|
+
swiftDefinition,
|
|
25
29
|
];
|
|
26
30
|
/**
|
|
27
31
|
* Canonical list of supported language IDs.
|
|
@@ -38,6 +42,8 @@ export const LANGUAGE_IDS = [
|
|
|
38
42
|
'java',
|
|
39
43
|
'csharp',
|
|
40
44
|
'ruby',
|
|
45
|
+
'kotlin',
|
|
46
|
+
'swift',
|
|
41
47
|
];
|
|
42
48
|
/**
|
|
43
49
|
* Registry keyed by language id.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/ast/languages/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAE/B,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/ast/languages/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAE/B,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C;;;GAGG;AACH,MAAM,WAAW,GAAyB;IACxC,oBAAoB;IACpB,oBAAoB;IACpB,aAAa;IACb,gBAAgB;IAChB,cAAc;IACd,YAAY;IACZ,cAAc;IACd,gBAAgB;IAChB,cAAc;IACd,gBAAgB;IAChB,eAAe;CAChB,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,YAAY;IACZ,YAAY;IACZ,KAAK;IACL,QAAQ;IACR,MAAM;IACN,IAAI;IACJ,MAAM;IACN,QAAQ;IACR,MAAM;IACN,QAAQ;IACR,OAAO;CACC,CAAC;AAGX;;GAEG;AACH,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAA8B,CAAC;AAC/D,MAAM,YAAY,GAAG,IAAI,GAAG,EAA6B,CAAC;AAE1D,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;IAC9B,IAAI,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,sCAAsC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;IAClE,CAAC;IACD,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IAElC,KAAK,MAAM,GAAG,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;QACjC,IAAI,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CACb,wBAAwB,GAAG,oBAAoB,GAAG,CAAC,EAAE,0BAA0B,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CACzG,CAAC;QACJ,CAAC;QACD,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IAChC,CAAC;AACH,CAAC;AAED,wEAAwE;AACxE,IAAI,IAAI,GAAG,CAAC,YAAY,CAAC,CAAC,IAAI,KAAK,YAAY,CAAC,MAAM,EAAE,CAAC;IACvD,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3F,MAAM,IAAI,KAAK,CAAC,4CAA4C,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAClF,CAAC;AACD,KAAK,MAAM,EAAE,IAAI,YAAY,EAAE,CAAC;IAC9B,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,aAAa,EAAE,4DAA4D,CAAC,CAAC;IAC/F,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,QAA2B;IACrD,MAAM,GAAG,GAAG,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC3C,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CAAC,0CAA0C,QAAQ,EAAE,CAAC,CAAC;IACxE,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB;IAC7C,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IACrD,OAAO,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;AACvC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB;IAC7C,OAAO,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACxC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe;IAC7B,OAAO,WAAW,CAAC,KAAK,EAAE,CAAC;AAC7B,CAAC;AAED;;;GAGG;AACH,IAAI,cAAc,GAAoB,IAAI,CAAC;AAE3C,MAAM,UAAU,sBAAsB;IACpC,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,cAAc,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,cAAc,CAAC;AACxB,CAAC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type Parser from 'tree-sitter';
|
|
2
|
+
import type { SymbolInfo } from '../types.js';
|
|
3
|
+
import type { LanguageDefinition } from './types.js';
|
|
4
|
+
import type { LanguageTraverser, DeclarationFunctionInfo } from '../traversers/types.js';
|
|
5
|
+
import type { LanguageExportExtractor, LanguageImportExtractor, LanguageSymbolExtractor } from '../extractors/types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Swift AST traverser.
|
|
8
|
+
*
|
|
9
|
+
* Functions/methods/initializers live inside `class_declaration` (which covers
|
|
10
|
+
* class / struct / actor / enum / extension) and `protocol_declaration` bodies
|
|
11
|
+
* (`class_body` / `enum_class_body` / `protocol_body`). Closures can appear as
|
|
12
|
+
* property initializers (`let handler = { … }`).
|
|
13
|
+
*/
|
|
14
|
+
export declare class SwiftTraverser implements LanguageTraverser {
|
|
15
|
+
targetNodeTypes: string[];
|
|
16
|
+
containerTypes: string[];
|
|
17
|
+
declarationTypes: string[];
|
|
18
|
+
functionTypes: string[];
|
|
19
|
+
shouldExtractChildren(node: Parser.SyntaxNode): boolean;
|
|
20
|
+
isDeclarationWithFunction(node: Parser.SyntaxNode): boolean;
|
|
21
|
+
getContainerBody(node: Parser.SyntaxNode): Parser.SyntaxNode | null;
|
|
22
|
+
shouldTraverseChildren(node: Parser.SyntaxNode): boolean;
|
|
23
|
+
findParentContainerName(node: Parser.SyntaxNode): string | undefined;
|
|
24
|
+
findFunctionInDeclaration(node: Parser.SyntaxNode): DeclarationFunctionInfo;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Swift export extractor.
|
|
28
|
+
*
|
|
29
|
+
* Swift has no `export` keyword. Top-level declarations and (non-private/
|
|
30
|
+
* fileprivate) members form the file's API surface. Protocol members are
|
|
31
|
+
* implicitly part of the protocol's surface.
|
|
32
|
+
*/
|
|
33
|
+
export declare class SwiftExportExtractor implements LanguageExportExtractor {
|
|
34
|
+
extractExports(rootNode: Parser.SyntaxNode): string[];
|
|
35
|
+
private extractFromNode;
|
|
36
|
+
private extractMembers;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Swift import extractor.
|
|
40
|
+
*
|
|
41
|
+
* Handles `import Foundation`, dotted `import Combine.Just`, and import-kind
|
|
42
|
+
* forms (`import struct Combine.Just` — the `struct`/`class`/… keyword is an
|
|
43
|
+
* anonymous token and is ignored; the module path is what matters). Only the
|
|
44
|
+
* `Swift` stdlib module is filtered out.
|
|
45
|
+
*/
|
|
46
|
+
export declare class SwiftImportExtractor implements LanguageImportExtractor {
|
|
47
|
+
readonly importNodeTypes: string[];
|
|
48
|
+
extractImportPath(node: Parser.SyntaxNode): string | null;
|
|
49
|
+
processImportSymbols(node: Parser.SyntaxNode): {
|
|
50
|
+
importPath: string;
|
|
51
|
+
symbols: string[];
|
|
52
|
+
} | null;
|
|
53
|
+
private getImportPath;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Swift symbol extractor.
|
|
57
|
+
*
|
|
58
|
+
* Handles `function_declaration`, `init`/`deinit`/`subscript` declarations,
|
|
59
|
+
* `class_declaration` (class / struct / actor / enum / extension — distinguished
|
|
60
|
+
* by the `declaration_kind` keyword), and `protocol_declaration`. `SymbolInfo.type`
|
|
61
|
+
* only has `function|method|class|interface`, so struct/actor/enum/extension map
|
|
62
|
+
* to `class` (keeping the real keyword in the signature) and protocol maps to
|
|
63
|
+
* `interface`.
|
|
64
|
+
*
|
|
65
|
+
* Call sites: `call_expression` — `foo()` (simple_identifier callee) and
|
|
66
|
+
* `a.b.c()` (the member is the `simple_identifier` in the trailing
|
|
67
|
+
* `navigation_suffix`).
|
|
68
|
+
*/
|
|
69
|
+
export declare class SwiftSymbolExtractor implements LanguageSymbolExtractor {
|
|
70
|
+
readonly symbolNodeTypes: string[];
|
|
71
|
+
extractSymbol(node: Parser.SyntaxNode, content: string, parentClass?: string): SymbolInfo | null;
|
|
72
|
+
/**
|
|
73
|
+
* Resolve a call site to the called symbol name. Dependency matching is by
|
|
74
|
+
* bare name (call.symbol === symbol.name), so the name we record determines
|
|
75
|
+
* which definition the call links to.
|
|
76
|
+
*
|
|
77
|
+
* Constructor calls are worth a note: Swift spells initialization `Foo(...)`
|
|
78
|
+
* (no `new`), so the callee is the type name and we record `Foo` — linking the
|
|
79
|
+
* call to the type's `class_declaration` symbol, exactly as JS records
|
|
80
|
+
* `new Foo()` against the class and Python records `Foo()`. So blast-radius for
|
|
81
|
+
* "who constructs Foo" is found via the TYPE symbol (a safe over-approximation),
|
|
82
|
+
* not the `init` method symbol. An explicit `Foo.init()` records `init` and
|
|
83
|
+
* links the initializer directly. The standalone `init`/`deinit`/`subscript`
|
|
84
|
+
* symbols are definitions for search/listing (as Java emits constructor
|
|
85
|
+
* symbols); they intentionally carry no implicit-`Foo()` caller edges.
|
|
86
|
+
*/
|
|
87
|
+
extractCallSite(node: Parser.SyntaxNode): {
|
|
88
|
+
symbol: string;
|
|
89
|
+
line: number;
|
|
90
|
+
key: string;
|
|
91
|
+
} | null;
|
|
92
|
+
private extractFunctionInfo;
|
|
93
|
+
private extractClassInfo;
|
|
94
|
+
private extractProtocolInfo;
|
|
95
|
+
private makeSymbol;
|
|
96
|
+
}
|
|
97
|
+
export declare const swiftDefinition: LanguageDefinition;
|
|
98
|
+
//# sourceMappingURL=swift.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"swift.d.ts","sourceRoot":"","sources":["../../../src/ast/languages/swift.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACzF,OAAO,KAAK,EACV,uBAAuB,EACvB,uBAAuB,EACvB,uBAAuB,EACxB,MAAM,wBAAwB,CAAC;AA4GhC;;;;;;;GAOG;AACH,qBAAa,cAAe,YAAW,iBAAiB;IACtD,eAAe,WAMb;IAEF,cAAc,WAAiD;IAE/D,gBAAgB,WAA4B;IAE5C,aAAa,WAAsB;IAEnC,qBAAqB,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,GAAG,OAAO;IAIvD,yBAAyB,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,GAAG,OAAO;IAI3D,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,GAAG,IAAI;IAKnE,sBAAsB,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,GAAG,OAAO;IASxD,uBAAuB,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,GAAG,SAAS;IAWpE,yBAAyB,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,GAAG,uBAAuB;CAS5E;AAMD;;;;;;GAMG;AACH,qBAAa,oBAAqB,YAAW,uBAAuB;IAClE,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,EAAE;IAgBrD,OAAO,CAAC,eAAe;IAsBvB,OAAO,CAAC,cAAc;CA4BvB;AAcD;;;;;;;GAOG;AACH,qBAAa,oBAAqB,YAAW,uBAAuB;IAClE,QAAQ,CAAC,eAAe,WAA0B;IAElD,iBAAiB,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,GAAG,IAAI;IAMzD,oBAAoB,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,GAAG;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,IAAI;IAS/F,OAAO,CAAC,aAAa;CAGtB;AAMD;;;;;;;;;;;;;GAaG;AACH,qBAAa,oBAAqB,YAAW,uBAAuB;IAClE,QAAQ,CAAC,eAAe,WAQtB;IAEF,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAiBhG;;;;;;;;;;;;;;OAcG;IACH,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,GAAG;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAqB9F,OAAO,CAAC,mBAAmB;IAqB3B,OAAO,CAAC,gBAAgB;IAOxB,OAAO,CAAC,mBAAmB;IAM3B,OAAO,CAAC,UAAU;CAcnB;AAMD,eAAO,MAAM,eAAe,EAAE,kBA0I7B,CAAC"}
|
|
@@ -0,0 +1,529 @@
|
|
|
1
|
+
import Swift from 'tree-sitter-swift';
|
|
2
|
+
import { extractSignature, extractReturnType } from '../extractors/symbol-helpers.js';
|
|
3
|
+
import { calculateComplexity } from '../complexity/index.js';
|
|
4
|
+
// =============================================================================
|
|
5
|
+
// HELPERS
|
|
6
|
+
//
|
|
7
|
+
// The tree-sitter-swift grammar (alex-pinkus) DOES expose field names
|
|
8
|
+
// (`name`, `body`, `return_type`, `declaration_kind`, …), so — unlike Kotlin —
|
|
9
|
+
// we use `childForFieldName` like the Java definition. The Swift-specific
|
|
10
|
+
// wrinkles are: class/struct/actor/enum/extension all share the
|
|
11
|
+
// `class_declaration` node (distinguished by the `declaration_kind` keyword),
|
|
12
|
+
// parameters are bare `parameter` children (no `parameters` wrapper field), and
|
|
13
|
+
// visibility lives under a `modifiers` child.
|
|
14
|
+
// =============================================================================
|
|
15
|
+
/** First named child of a given type. */
|
|
16
|
+
function childByType(node, type) {
|
|
17
|
+
return node.namedChildren.find(child => child.type === type) ?? null;
|
|
18
|
+
}
|
|
19
|
+
/** First descendant of a given type (depth-first), or null. */
|
|
20
|
+
function findFirst(node, type) {
|
|
21
|
+
for (const child of node.namedChildren) {
|
|
22
|
+
if (child.type === type)
|
|
23
|
+
return child;
|
|
24
|
+
const found = findFirst(child, type);
|
|
25
|
+
if (found)
|
|
26
|
+
return found;
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The `class_declaration` keyword (`class` / `struct` / `actor` / `enum` /
|
|
32
|
+
* `extension`), read from the `declaration_kind` field. Defaults to `class`.
|
|
33
|
+
*/
|
|
34
|
+
function declarationKeyword(node) {
|
|
35
|
+
return node.childForFieldName('declaration_kind')?.text ?? 'class';
|
|
36
|
+
}
|
|
37
|
+
/** The declared name of a class/protocol declaration (handles `extension`'s `user_type`). */
|
|
38
|
+
function declarationName(node) {
|
|
39
|
+
return node.childForFieldName('name')?.text;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The name of a function-like node. Swift's `init`/`deinit`/`subscript`
|
|
43
|
+
* declarations have no `name` field — fall back to the keyword.
|
|
44
|
+
*/
|
|
45
|
+
function functionLikeName(node) {
|
|
46
|
+
const name = node.childForFieldName('name')?.text;
|
|
47
|
+
if (name)
|
|
48
|
+
return name;
|
|
49
|
+
switch (node.type) {
|
|
50
|
+
case 'init_declaration':
|
|
51
|
+
return 'init';
|
|
52
|
+
case 'deinit_declaration':
|
|
53
|
+
return 'deinit';
|
|
54
|
+
case 'subscript_declaration':
|
|
55
|
+
return 'subscript';
|
|
56
|
+
default:
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** Parameter texts. Swift function params are bare `parameter` children (no field). */
|
|
61
|
+
function swiftParameters(node) {
|
|
62
|
+
return node.namedChildren.filter(c => c.type === 'parameter' && c.text.trim()).map(c => c.text);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The function-valued initializer of a property, if the property's value is
|
|
66
|
+
* *directly* a closure (`let handler = { … }`). Checks the `value` field rather
|
|
67
|
+
* than any descendant, so a closure passed as a call argument
|
|
68
|
+
* (`let n = xs.map { … }`) is NOT treated as a function-valued property.
|
|
69
|
+
*/
|
|
70
|
+
function propertyInitializerFunction(node) {
|
|
71
|
+
const value = node.childForFieldName('value');
|
|
72
|
+
return value?.type === 'lambda_literal' ? value : null;
|
|
73
|
+
}
|
|
74
|
+
/** Visibility modifier text (`public` / `private` / `fileprivate` / `internal` / `open`), if any. */
|
|
75
|
+
function visibilityModifier(node) {
|
|
76
|
+
const modifiers = childByType(node, 'modifiers');
|
|
77
|
+
if (!modifiers)
|
|
78
|
+
return null;
|
|
79
|
+
return modifiers.namedChildren.find(c => c.type === 'visibility_modifier')?.text ?? null;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Swift declarations are `internal` (module-visible) by default. We treat a
|
|
83
|
+
* declaration as "exported" (part of the file's API surface) unless it is
|
|
84
|
+
* explicitly `private` or `fileprivate` — so default/`internal`/`public`/`open`
|
|
85
|
+
* are surfaced. (Analogous to Kotlin's "public unless private/internal".)
|
|
86
|
+
*/
|
|
87
|
+
function isExported(node) {
|
|
88
|
+
const visibility = visibilityModifier(node);
|
|
89
|
+
return visibility !== 'private' && visibility !== 'fileprivate';
|
|
90
|
+
}
|
|
91
|
+
/** The property's bound identifier (`let foo = …` → `foo`). */
|
|
92
|
+
function propertyName(node) {
|
|
93
|
+
const pattern = node.childForFieldName('name');
|
|
94
|
+
if (!pattern)
|
|
95
|
+
return undefined;
|
|
96
|
+
return findFirst(pattern, 'simple_identifier')?.text ?? pattern.text;
|
|
97
|
+
}
|
|
98
|
+
// =============================================================================
|
|
99
|
+
// TRAVERSER
|
|
100
|
+
// =============================================================================
|
|
101
|
+
/**
|
|
102
|
+
* Swift AST traverser.
|
|
103
|
+
*
|
|
104
|
+
* Functions/methods/initializers live inside `class_declaration` (which covers
|
|
105
|
+
* class / struct / actor / enum / extension) and `protocol_declaration` bodies
|
|
106
|
+
* (`class_body` / `enum_class_body` / `protocol_body`). Closures can appear as
|
|
107
|
+
* property initializers (`let handler = { … }`).
|
|
108
|
+
*/
|
|
109
|
+
export class SwiftTraverser {
|
|
110
|
+
targetNodeTypes = [
|
|
111
|
+
'function_declaration',
|
|
112
|
+
'protocol_function_declaration', // abstract method requirements inside a protocol
|
|
113
|
+
'init_declaration',
|
|
114
|
+
'deinit_declaration',
|
|
115
|
+
'subscript_declaration',
|
|
116
|
+
];
|
|
117
|
+
containerTypes = ['class_declaration', 'protocol_declaration'];
|
|
118
|
+
declarationTypes = ['property_declaration'];
|
|
119
|
+
functionTypes = ['lambda_literal'];
|
|
120
|
+
shouldExtractChildren(node) {
|
|
121
|
+
return this.containerTypes.includes(node.type);
|
|
122
|
+
}
|
|
123
|
+
isDeclarationWithFunction(node) {
|
|
124
|
+
return node.type === 'property_declaration' && propertyInitializerFunction(node) !== null;
|
|
125
|
+
}
|
|
126
|
+
getContainerBody(node) {
|
|
127
|
+
if (!this.containerTypes.includes(node.type))
|
|
128
|
+
return null;
|
|
129
|
+
return node.childForFieldName('body');
|
|
130
|
+
}
|
|
131
|
+
shouldTraverseChildren(node) {
|
|
132
|
+
return (node.type === 'source_file' ||
|
|
133
|
+
node.type === 'class_body' ||
|
|
134
|
+
node.type === 'enum_class_body' ||
|
|
135
|
+
node.type === 'protocol_body');
|
|
136
|
+
}
|
|
137
|
+
findParentContainerName(node) {
|
|
138
|
+
let current = node.parent;
|
|
139
|
+
while (current) {
|
|
140
|
+
if (current.type === 'class_declaration' || current.type === 'protocol_declaration') {
|
|
141
|
+
return declarationName(current);
|
|
142
|
+
}
|
|
143
|
+
current = current.parent;
|
|
144
|
+
}
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
findFunctionInDeclaration(node) {
|
|
148
|
+
if (node.type !== 'property_declaration') {
|
|
149
|
+
return { hasFunction: false, functionNode: null };
|
|
150
|
+
}
|
|
151
|
+
const fn = propertyInitializerFunction(node);
|
|
152
|
+
return fn
|
|
153
|
+
? { hasFunction: true, functionNode: fn }
|
|
154
|
+
: { hasFunction: false, functionNode: null };
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// =============================================================================
|
|
158
|
+
// EXPORT EXTRACTOR
|
|
159
|
+
// =============================================================================
|
|
160
|
+
/**
|
|
161
|
+
* Swift export extractor.
|
|
162
|
+
*
|
|
163
|
+
* Swift has no `export` keyword. Top-level declarations and (non-private/
|
|
164
|
+
* fileprivate) members form the file's API surface. Protocol members are
|
|
165
|
+
* implicitly part of the protocol's surface.
|
|
166
|
+
*/
|
|
167
|
+
export class SwiftExportExtractor {
|
|
168
|
+
extractExports(rootNode) {
|
|
169
|
+
const exports = [];
|
|
170
|
+
const seen = new Set();
|
|
171
|
+
const addExport = (name) => {
|
|
172
|
+
if (name && !seen.has(name)) {
|
|
173
|
+
seen.add(name);
|
|
174
|
+
exports.push(name);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
rootNode.namedChildren.forEach(child => this.extractFromNode(child, addExport));
|
|
178
|
+
return exports;
|
|
179
|
+
}
|
|
180
|
+
extractFromNode(node, addExport) {
|
|
181
|
+
switch (node.type) {
|
|
182
|
+
case 'function_declaration':
|
|
183
|
+
case 'init_declaration':
|
|
184
|
+
case 'deinit_declaration':
|
|
185
|
+
case 'subscript_declaration':
|
|
186
|
+
if (isExported(node))
|
|
187
|
+
addExport(functionLikeName(node));
|
|
188
|
+
break;
|
|
189
|
+
case 'property_declaration':
|
|
190
|
+
if (isExported(node))
|
|
191
|
+
addExport(propertyName(node));
|
|
192
|
+
break;
|
|
193
|
+
case 'class_declaration':
|
|
194
|
+
case 'protocol_declaration':
|
|
195
|
+
// A private/fileprivate container exposes nothing — skip it and its members.
|
|
196
|
+
if (isExported(node)) {
|
|
197
|
+
addExport(declarationName(node));
|
|
198
|
+
this.extractMembers(node, addExport);
|
|
199
|
+
}
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
extractMembers(container, addExport) {
|
|
204
|
+
const body = container.childForFieldName('body');
|
|
205
|
+
if (!body)
|
|
206
|
+
return;
|
|
207
|
+
// Protocol members are implicitly part of the protocol's surface.
|
|
208
|
+
const isProtocol = container.type === 'protocol_declaration';
|
|
209
|
+
body.namedChildren.forEach(member => {
|
|
210
|
+
switch (member.type) {
|
|
211
|
+
case 'function_declaration':
|
|
212
|
+
case 'protocol_function_declaration':
|
|
213
|
+
case 'init_declaration':
|
|
214
|
+
case 'deinit_declaration':
|
|
215
|
+
case 'subscript_declaration':
|
|
216
|
+
if (isProtocol || isExported(member))
|
|
217
|
+
addExport(functionLikeName(member));
|
|
218
|
+
break;
|
|
219
|
+
case 'property_declaration':
|
|
220
|
+
case 'protocol_property_declaration':
|
|
221
|
+
if (isProtocol || isExported(member))
|
|
222
|
+
addExport(propertyName(member));
|
|
223
|
+
break;
|
|
224
|
+
case 'class_declaration':
|
|
225
|
+
case 'protocol_declaration':
|
|
226
|
+
// Nested types — recurse so their names/members are surfaced too.
|
|
227
|
+
this.extractFromNode(member, addExport);
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
// =============================================================================
|
|
234
|
+
// IMPORT EXTRACTOR
|
|
235
|
+
// =============================================================================
|
|
236
|
+
/**
|
|
237
|
+
* The Swift standard library module. `Foundation`, `UIKit`, `SwiftUI`,
|
|
238
|
+
* `Combine`, etc. are real external frameworks and are kept as import edges.
|
|
239
|
+
*/
|
|
240
|
+
function isSwiftStdLib(importPath) {
|
|
241
|
+
return importPath === 'Swift' || importPath.startsWith('Swift.');
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Swift import extractor.
|
|
245
|
+
*
|
|
246
|
+
* Handles `import Foundation`, dotted `import Combine.Just`, and import-kind
|
|
247
|
+
* forms (`import struct Combine.Just` — the `struct`/`class`/… keyword is an
|
|
248
|
+
* anonymous token and is ignored; the module path is what matters). Only the
|
|
249
|
+
* `Swift` stdlib module is filtered out.
|
|
250
|
+
*/
|
|
251
|
+
export class SwiftImportExtractor {
|
|
252
|
+
importNodeTypes = ['import_declaration'];
|
|
253
|
+
extractImportPath(node) {
|
|
254
|
+
const path = this.getImportPath(node);
|
|
255
|
+
if (!path || isSwiftStdLib(path))
|
|
256
|
+
return null;
|
|
257
|
+
return path;
|
|
258
|
+
}
|
|
259
|
+
processImportSymbols(node) {
|
|
260
|
+
const path = this.getImportPath(node);
|
|
261
|
+
if (!path || isSwiftStdLib(path))
|
|
262
|
+
return null;
|
|
263
|
+
const parts = path.split('.');
|
|
264
|
+
const lastPart = parts[parts.length - 1];
|
|
265
|
+
return { importPath: path, symbols: [lastPart] };
|
|
266
|
+
}
|
|
267
|
+
getImportPath(node) {
|
|
268
|
+
return childByType(node, 'identifier')?.text ?? null;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
// =============================================================================
|
|
272
|
+
// SYMBOL EXTRACTOR
|
|
273
|
+
// =============================================================================
|
|
274
|
+
/**
|
|
275
|
+
* Swift symbol extractor.
|
|
276
|
+
*
|
|
277
|
+
* Handles `function_declaration`, `init`/`deinit`/`subscript` declarations,
|
|
278
|
+
* `class_declaration` (class / struct / actor / enum / extension — distinguished
|
|
279
|
+
* by the `declaration_kind` keyword), and `protocol_declaration`. `SymbolInfo.type`
|
|
280
|
+
* only has `function|method|class|interface`, so struct/actor/enum/extension map
|
|
281
|
+
* to `class` (keeping the real keyword in the signature) and protocol maps to
|
|
282
|
+
* `interface`.
|
|
283
|
+
*
|
|
284
|
+
* Call sites: `call_expression` — `foo()` (simple_identifier callee) and
|
|
285
|
+
* `a.b.c()` (the member is the `simple_identifier` in the trailing
|
|
286
|
+
* `navigation_suffix`).
|
|
287
|
+
*/
|
|
288
|
+
export class SwiftSymbolExtractor {
|
|
289
|
+
symbolNodeTypes = [
|
|
290
|
+
'function_declaration',
|
|
291
|
+
'protocol_function_declaration',
|
|
292
|
+
'init_declaration',
|
|
293
|
+
'deinit_declaration',
|
|
294
|
+
'subscript_declaration',
|
|
295
|
+
'class_declaration',
|
|
296
|
+
'protocol_declaration',
|
|
297
|
+
];
|
|
298
|
+
extractSymbol(node, content, parentClass) {
|
|
299
|
+
switch (node.type) {
|
|
300
|
+
case 'function_declaration':
|
|
301
|
+
case 'protocol_function_declaration':
|
|
302
|
+
case 'init_declaration':
|
|
303
|
+
case 'deinit_declaration':
|
|
304
|
+
case 'subscript_declaration':
|
|
305
|
+
return this.extractFunctionInfo(node, content, parentClass);
|
|
306
|
+
case 'class_declaration':
|
|
307
|
+
return this.extractClassInfo(node);
|
|
308
|
+
case 'protocol_declaration':
|
|
309
|
+
return this.extractProtocolInfo(node);
|
|
310
|
+
default:
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Resolve a call site to the called symbol name. Dependency matching is by
|
|
316
|
+
* bare name (call.symbol === symbol.name), so the name we record determines
|
|
317
|
+
* which definition the call links to.
|
|
318
|
+
*
|
|
319
|
+
* Constructor calls are worth a note: Swift spells initialization `Foo(...)`
|
|
320
|
+
* (no `new`), so the callee is the type name and we record `Foo` — linking the
|
|
321
|
+
* call to the type's `class_declaration` symbol, exactly as JS records
|
|
322
|
+
* `new Foo()` against the class and Python records `Foo()`. So blast-radius for
|
|
323
|
+
* "who constructs Foo" is found via the TYPE symbol (a safe over-approximation),
|
|
324
|
+
* not the `init` method symbol. An explicit `Foo.init()` records `init` and
|
|
325
|
+
* links the initializer directly. The standalone `init`/`deinit`/`subscript`
|
|
326
|
+
* symbols are definitions for search/listing (as Java emits constructor
|
|
327
|
+
* symbols); they intentionally carry no implicit-`Foo()` caller edges.
|
|
328
|
+
*/
|
|
329
|
+
extractCallSite(node) {
|
|
330
|
+
if (node.type !== 'call_expression')
|
|
331
|
+
return null;
|
|
332
|
+
const line = node.startPosition.row + 1;
|
|
333
|
+
const callee = node.namedChild(0);
|
|
334
|
+
if (!callee)
|
|
335
|
+
return null;
|
|
336
|
+
let name;
|
|
337
|
+
if (callee.type === 'simple_identifier') {
|
|
338
|
+
name = callee.text; // foo() / Foo() constructor — links to the fn or type symbol
|
|
339
|
+
}
|
|
340
|
+
else if (callee.type === 'navigation_expression') {
|
|
341
|
+
// a.b.c() / Mod.Type() / Type.init() — the called member is the
|
|
342
|
+
// simple_identifier in the last navigation_suffix
|
|
343
|
+
const suffix = callee.namedChildren.filter(c => c.type === 'navigation_suffix').at(-1);
|
|
344
|
+
name = suffix ? (findFirst(suffix, 'simple_identifier')?.text ?? undefined) : undefined;
|
|
345
|
+
}
|
|
346
|
+
if (!name)
|
|
347
|
+
return null;
|
|
348
|
+
return { symbol: name, line, key: `${name}:${line}` };
|
|
349
|
+
}
|
|
350
|
+
extractFunctionInfo(node, content, parentClass) {
|
|
351
|
+
const name = functionLikeName(node);
|
|
352
|
+
if (!name)
|
|
353
|
+
return null;
|
|
354
|
+
return {
|
|
355
|
+
name,
|
|
356
|
+
type: parentClass ? 'method' : 'function',
|
|
357
|
+
startLine: node.startPosition.row + 1,
|
|
358
|
+
endLine: node.endPosition.row + 1,
|
|
359
|
+
parentClass,
|
|
360
|
+
signature: extractSignature(node, content),
|
|
361
|
+
parameters: swiftParameters(node),
|
|
362
|
+
returnType: extractReturnType(node, content),
|
|
363
|
+
complexity: calculateComplexity(node),
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
extractClassInfo(node) {
|
|
367
|
+
const name = declarationName(node);
|
|
368
|
+
if (!name)
|
|
369
|
+
return null;
|
|
370
|
+
const keyword = declarationKeyword(node);
|
|
371
|
+
return this.makeSymbol(node, name, 'class', `${keyword} ${name}`);
|
|
372
|
+
}
|
|
373
|
+
extractProtocolInfo(node) {
|
|
374
|
+
const name = declarationName(node);
|
|
375
|
+
if (!name)
|
|
376
|
+
return null;
|
|
377
|
+
return this.makeSymbol(node, name, 'interface', `protocol ${name}`);
|
|
378
|
+
}
|
|
379
|
+
makeSymbol(node, name, type, signature) {
|
|
380
|
+
return {
|
|
381
|
+
name,
|
|
382
|
+
type,
|
|
383
|
+
startLine: node.startPosition.row + 1,
|
|
384
|
+
endLine: node.endPosition.row + 1,
|
|
385
|
+
signature,
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
// =============================================================================
|
|
390
|
+
// LANGUAGE DEFINITION
|
|
391
|
+
// =============================================================================
|
|
392
|
+
export const swiftDefinition = {
|
|
393
|
+
id: 'swift',
|
|
394
|
+
extensions: ['swift'],
|
|
395
|
+
grammar: Swift,
|
|
396
|
+
traverser: new SwiftTraverser(),
|
|
397
|
+
exportExtractor: new SwiftExportExtractor(),
|
|
398
|
+
importExtractor: new SwiftImportExtractor(),
|
|
399
|
+
symbolExtractor: new SwiftSymbolExtractor(),
|
|
400
|
+
complexity: {
|
|
401
|
+
decisionPoints: [
|
|
402
|
+
'if_statement',
|
|
403
|
+
'guard_statement',
|
|
404
|
+
'switch_entry', // each `case` / `default` branch
|
|
405
|
+
'for_statement',
|
|
406
|
+
'while_statement',
|
|
407
|
+
'repeat_while_statement',
|
|
408
|
+
'catch_block',
|
|
409
|
+
'conjunction_expression', // &&
|
|
410
|
+
'disjunction_expression', // ||
|
|
411
|
+
'nil_coalescing_expression', // ??
|
|
412
|
+
'ternary_expression',
|
|
413
|
+
],
|
|
414
|
+
nestingTypes: [
|
|
415
|
+
'if_statement',
|
|
416
|
+
'guard_statement',
|
|
417
|
+
'switch_statement',
|
|
418
|
+
'for_statement',
|
|
419
|
+
'while_statement',
|
|
420
|
+
'repeat_while_statement',
|
|
421
|
+
'do_statement',
|
|
422
|
+
'catch_block',
|
|
423
|
+
'lambda_literal',
|
|
424
|
+
],
|
|
425
|
+
nonNestingTypes: [
|
|
426
|
+
'switch_entry',
|
|
427
|
+
'conjunction_expression',
|
|
428
|
+
'disjunction_expression',
|
|
429
|
+
'nil_coalescing_expression',
|
|
430
|
+
'ternary_expression',
|
|
431
|
+
],
|
|
432
|
+
lambdaTypes: ['lambda_literal'],
|
|
433
|
+
operatorSymbols: new Set([
|
|
434
|
+
'+',
|
|
435
|
+
'-',
|
|
436
|
+
'*',
|
|
437
|
+
'/',
|
|
438
|
+
'%',
|
|
439
|
+
'==',
|
|
440
|
+
'!=',
|
|
441
|
+
'===',
|
|
442
|
+
'!==',
|
|
443
|
+
'<',
|
|
444
|
+
'>',
|
|
445
|
+
'<=',
|
|
446
|
+
'>=',
|
|
447
|
+
'=',
|
|
448
|
+
'+=',
|
|
449
|
+
'-=',
|
|
450
|
+
'*=',
|
|
451
|
+
'/=',
|
|
452
|
+
'%=',
|
|
453
|
+
'&&',
|
|
454
|
+
'||',
|
|
455
|
+
'!',
|
|
456
|
+
'??',
|
|
457
|
+
'?.',
|
|
458
|
+
'.',
|
|
459
|
+
'->',
|
|
460
|
+
'..<',
|
|
461
|
+
'...',
|
|
462
|
+
'(',
|
|
463
|
+
')',
|
|
464
|
+
'[',
|
|
465
|
+
']',
|
|
466
|
+
'{',
|
|
467
|
+
'}',
|
|
468
|
+
]),
|
|
469
|
+
operatorKeywords: new Set([
|
|
470
|
+
'if',
|
|
471
|
+
'else',
|
|
472
|
+
'guard',
|
|
473
|
+
'switch',
|
|
474
|
+
'case',
|
|
475
|
+
'default',
|
|
476
|
+
'for',
|
|
477
|
+
'while',
|
|
478
|
+
'repeat',
|
|
479
|
+
'do',
|
|
480
|
+
'return',
|
|
481
|
+
'break',
|
|
482
|
+
'continue',
|
|
483
|
+
'fallthrough',
|
|
484
|
+
'throw',
|
|
485
|
+
'throws',
|
|
486
|
+
'try',
|
|
487
|
+
'catch',
|
|
488
|
+
'defer',
|
|
489
|
+
'func',
|
|
490
|
+
'class',
|
|
491
|
+
'struct',
|
|
492
|
+
'actor',
|
|
493
|
+
'enum',
|
|
494
|
+
'protocol',
|
|
495
|
+
'extension',
|
|
496
|
+
'init',
|
|
497
|
+
'deinit',
|
|
498
|
+
'subscript',
|
|
499
|
+
'var',
|
|
500
|
+
'let',
|
|
501
|
+
'is',
|
|
502
|
+
'as',
|
|
503
|
+
'in',
|
|
504
|
+
'where',
|
|
505
|
+
'some',
|
|
506
|
+
'any',
|
|
507
|
+
'async',
|
|
508
|
+
'await',
|
|
509
|
+
'import',
|
|
510
|
+
'static',
|
|
511
|
+
'final',
|
|
512
|
+
'lazy',
|
|
513
|
+
'weak',
|
|
514
|
+
'override',
|
|
515
|
+
'private',
|
|
516
|
+
'fileprivate',
|
|
517
|
+
'internal',
|
|
518
|
+
'public',
|
|
519
|
+
'open',
|
|
520
|
+
'self',
|
|
521
|
+
'super',
|
|
522
|
+
'nil',
|
|
523
|
+
]),
|
|
524
|
+
},
|
|
525
|
+
symbols: {
|
|
526
|
+
callExpressionTypes: ['call_expression'],
|
|
527
|
+
},
|
|
528
|
+
};
|
|
529
|
+
//# sourceMappingURL=swift.js.map
|