clast-ai 0.1.2 → 0.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-plugin/marketplace.json +14 -0
- package/.claude-plugin/plugin.json +11 -9
- package/README.md +447 -401
- package/bench/ab/onair-concept.json +22 -0
- package/bench/ab/onair-deadcode.json +7 -0
- package/bench/ab/onair-hard.json +32 -0
- package/bench/ab/onair-impact.json +17 -0
- package/bench/ab/onair-refactors.json +18 -0
- package/bench/ab/onair-survey.json +17 -0
- package/bench/ab/onair-tasks.json +20 -0
- package/bench/ab/onair.json +52 -0
- package/bench/ab/vscode-concept.json +22 -0
- package/bench/ab/vscode-hard.json +32 -0
- package/bench/ab/vscode-impact.json +17 -0
- package/bench/ab/vscode-refactors.json +18 -0
- package/bench/ab/vscode-tasks.json +20 -0
- package/bench/ab/vscode.json +52 -0
- package/bench/ab.mjs +213 -0
- package/bench/bench.mjs +98 -0
- package/bench/resolution.mjs +75 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +257 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/defaults.d.ts +12 -2
- package/dist/config/defaults.js +38 -31
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.js +26 -14
- package/dist/config/index.js.map +1 -1
- package/dist/db/index.d.ts +73 -13
- package/dist/db/index.js +481 -39
- package/dist/db/index.js.map +1 -1
- package/dist/engine.d.ts +15 -0
- package/dist/engine.js +28 -0
- package/dist/engine.js.map +1 -0
- package/dist/indexer/imports.d.ts +6 -0
- package/dist/indexer/imports.js +254 -0
- package/dist/indexer/imports.js.map +1 -0
- package/dist/indexer/index.d.ts +63 -0
- package/dist/indexer/index.js +360 -0
- package/dist/indexer/index.js.map +1 -0
- package/dist/indexer/process.d.ts +22 -0
- package/dist/indexer/process.js +42 -0
- package/dist/indexer/process.js.map +1 -0
- package/dist/indexer/scan.d.ts +43 -0
- package/dist/indexer/scan.js +223 -0
- package/dist/indexer/scan.js.map +1 -0
- package/dist/indexer/worker.d.ts +1 -0
- package/dist/indexer/worker.js +23 -0
- package/dist/indexer/worker.js.map +1 -0
- package/dist/llm/comment-generator.d.ts +17 -19
- package/dist/llm/comment-generator.js +46 -80
- package/dist/llm/comment-generator.js.map +1 -1
- package/dist/llm/index.d.ts +0 -1
- package/dist/llm/index.js.map +1 -1
- package/dist/llm/prompts.d.ts +0 -1
- package/dist/llm/prompts.js +16 -16
- package/dist/parser/extractor.d.ts +21 -16
- package/dist/parser/extractor.js +700 -228
- package/dist/parser/extractor.js.map +1 -1
- package/dist/parser/index.d.ts +17 -21
- package/dist/parser/index.js +115 -171
- package/dist/parser/index.js.map +1 -1
- package/dist/parser/langs/c.d.ts +3 -0
- package/dist/parser/langs/c.js +879 -0
- package/dist/parser/langs/c.js.map +1 -0
- package/dist/parser/langs/csharp.d.ts +2 -0
- package/dist/parser/langs/csharp.js +548 -0
- package/dist/parser/langs/csharp.js.map +1 -0
- package/dist/parser/langs/dart.d.ts +2 -0
- package/dist/parser/langs/dart.js +893 -0
- package/dist/parser/langs/dart.js.map +1 -0
- package/dist/parser/langs/go.d.ts +2 -0
- package/dist/parser/langs/go.js +455 -0
- package/dist/parser/langs/go.js.map +1 -0
- package/dist/parser/langs/java.d.ts +2 -0
- package/dist/parser/langs/java.js +390 -0
- package/dist/parser/langs/java.js.map +1 -0
- package/dist/parser/langs/php.d.ts +2 -0
- package/dist/parser/langs/php.js +486 -0
- package/dist/parser/langs/php.js.map +1 -0
- package/dist/parser/langs/python.d.ts +2 -0
- package/dist/parser/langs/python.js +216 -0
- package/dist/parser/langs/python.js.map +1 -0
- package/dist/parser/langs/ruby.d.ts +2 -0
- package/dist/parser/langs/ruby.js +602 -0
- package/dist/parser/langs/ruby.js.map +1 -0
- package/dist/parser/langs/rust.d.ts +2 -0
- package/dist/parser/langs/rust.js +931 -0
- package/dist/parser/langs/rust.js.map +1 -0
- package/dist/parser/langs/swift.d.ts +2 -0
- package/dist/parser/langs/swift.js +616 -0
- package/dist/parser/langs/swift.js.map +1 -0
- package/dist/parser/langs/typescript.d.ts +4 -0
- package/dist/parser/langs/typescript.js +535 -0
- package/dist/parser/langs/typescript.js.map +1 -0
- package/dist/parser/registry.d.ts +16 -0
- package/dist/parser/registry.js +87 -0
- package/dist/parser/registry.js.map +1 -0
- package/dist/parser/returns.d.ts +9 -0
- package/dist/parser/returns.js +152 -0
- package/dist/parser/returns.js.map +1 -0
- package/dist/parser/spec.d.ts +165 -0
- package/dist/parser/spec.js +188 -0
- package/dist/parser/spec.js.map +1 -0
- package/dist/query/arity.d.ts +27 -0
- package/dist/query/arity.js +187 -0
- package/dist/query/arity.js.map +1 -0
- package/dist/query/callgraph.d.ts +113 -0
- package/dist/query/callgraph.js +332 -0
- package/dist/query/callgraph.js.map +1 -0
- package/dist/query/format.d.ts +36 -0
- package/dist/query/format.js +136 -0
- package/dist/query/format.js.map +1 -0
- package/dist/query/hierarchy.d.ts +44 -0
- package/dist/query/hierarchy.js +441 -0
- package/dist/query/hierarchy.js.map +1 -0
- package/dist/query/resolve.d.ts +265 -0
- package/dist/query/resolve.js +1957 -0
- package/dist/query/resolve.js.map +1 -0
- package/dist/query/search.d.ts +35 -0
- package/dist/query/search.js +379 -0
- package/dist/query/search.js.map +1 -0
- package/dist/query/words.d.ts +30 -0
- package/dist/query/words.js +201 -0
- package/dist/query/words.js.map +1 -0
- package/dist/server/index.d.ts +1 -3
- package/dist/server/index.js +313 -73
- package/dist/server/index.js.map +1 -1
- package/dist/tools/context.d.ts +35 -0
- package/dist/tools/context.js +79 -0
- package/dist/tools/context.js.map +1 -0
- package/dist/tools/explore.d.ts +51 -0
- package/dist/tools/explore.js +779 -0
- package/dist/tools/explore.js.map +1 -0
- package/dist/tools/impact.d.ts +21 -0
- package/dist/tools/impact.js +743 -0
- package/dist/tools/impact.js.map +1 -0
- package/dist/tools/search.d.ts +59 -0
- package/dist/tools/search.js +843 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/tools/stats.d.ts +26 -0
- package/dist/tools/stats.js +604 -0
- package/dist/tools/stats.js.map +1 -0
- package/dist/types.d.ts +95 -40
- package/dist/types.js +9 -1
- package/dist/types.js.map +1 -1
- package/dist/version.d.ts +2 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/dist/watcher/index.d.ts +18 -16
- package/dist/watcher/index.js +94 -77
- package/dist/watcher/index.js.map +1 -1
- package/package.json +21 -24
- package/plugin/server.mjs +50 -0
- package/skills/audit/SKILL.md +37 -0
- package/skills/deadcode/SKILL.md +33 -0
- package/skills/explore/SKILL.md +54 -0
- package/skills/impact/SKILL.md +24 -0
- package/skills/status/SKILL.md +11 -0
- package/src/cli.ts +256 -0
- package/src/config/defaults.ts +50 -32
- package/src/config/index.ts +23 -14
- package/src/db/index.ts +540 -43
- package/src/engine.ts +30 -0
- package/src/indexer/imports.ts +241 -0
- package/src/indexer/index.ts +369 -0
- package/src/indexer/process.ts +63 -0
- package/src/indexer/scan.ts +226 -0
- package/src/indexer/worker.ts +24 -0
- package/src/llm/comment-generator.ts +52 -103
- package/src/llm/index.ts +80 -80
- package/src/llm/prompts.ts +95 -95
- package/src/parser/extractor.ts +643 -256
- package/src/parser/index.ts +105 -205
- package/src/parser/langs/c.ts +860 -0
- package/src/parser/langs/csharp.ts +519 -0
- package/src/parser/langs/dart.ts +820 -0
- package/src/parser/langs/go.ts +437 -0
- package/src/parser/langs/java.ts +361 -0
- package/src/parser/langs/php.ts +450 -0
- package/src/parser/langs/python.ts +206 -0
- package/src/parser/langs/ruby.ts +554 -0
- package/src/parser/langs/rust.ts +904 -0
- package/src/parser/langs/swift.ts +590 -0
- package/src/parser/langs/typescript.ts +499 -0
- package/src/parser/registry.ts +88 -0
- package/src/parser/returns.ts +137 -0
- package/src/parser/spec.ts +309 -0
- package/src/query/arity.ts +173 -0
- package/src/query/callgraph.ts +473 -0
- package/src/query/format.ts +136 -0
- package/src/query/hierarchy.ts +447 -0
- package/src/query/resolve.ts +1972 -0
- package/src/query/search.ts +430 -0
- package/src/query/words.ts +183 -0
- package/src/server/index.ts +390 -79
- package/src/tools/context.ts +82 -0
- package/src/tools/explore.ts +860 -0
- package/src/tools/impact.ts +748 -0
- package/src/tools/search.ts +886 -0
- package/src/tools/stats.ts +633 -0
- package/src/types.ts +113 -45
- package/src/version.ts +2 -0
- package/src/watcher/index.ts +82 -88
- package/tsconfig.json +7 -4
- package/.mcp.json +0 -8
- package/commands/clast-status.md +0 -9
- package/dist/config/defaults.d.ts.map +0 -1
- package/dist/config/index.d.ts.map +0 -1
- package/dist/db/index.d.ts.map +0 -1
- package/dist/db/queries.d.ts +0 -44
- package/dist/db/queries.d.ts.map +0 -1
- package/dist/db/queries.js +0 -251
- package/dist/db/queries.js.map +0 -1
- package/dist/db/schema.d.ts +0 -3
- package/dist/db/schema.d.ts.map +0 -1
- package/dist/db/schema.js +0 -94
- package/dist/db/schema.js.map +0 -1
- package/dist/db/types.d.ts +0 -44
- package/dist/db/types.d.ts.map +0 -1
- package/dist/db/types.js +0 -2
- package/dist/db/types.js.map +0 -1
- package/dist/llm/comment-generator.d.ts.map +0 -1
- package/dist/llm/index.d.ts.map +0 -1
- package/dist/llm/prompts.d.ts.map +0 -1
- package/dist/parser/call-resolver.d.ts +0 -15
- package/dist/parser/call-resolver.d.ts.map +0 -1
- package/dist/parser/call-resolver.js +0 -16
- package/dist/parser/call-resolver.js.map +0 -1
- package/dist/parser/extractor.d.ts.map +0 -1
- package/dist/parser/index.d.ts.map +0 -1
- package/dist/parser/languages.d.ts +0 -11
- package/dist/parser/languages.d.ts.map +0 -1
- package/dist/parser/languages.js +0 -74
- package/dist/parser/languages.js.map +0 -1
- package/dist/server/index.d.ts.map +0 -1
- package/dist/server/register-tools.d.ts +0 -7
- package/dist/server/register-tools.d.ts.map +0 -1
- package/dist/server/register-tools.js +0 -17
- package/dist/server/register-tools.js.map +0 -1
- package/dist/server/tools/clast-call-graph.d.ts +0 -4
- package/dist/server/tools/clast-call-graph.d.ts.map +0 -1
- package/dist/server/tools/clast-call-graph.js +0 -70
- package/dist/server/tools/clast-call-graph.js.map +0 -1
- package/dist/server/tools/clast-comment.d.ts +0 -5
- package/dist/server/tools/clast-comment.d.ts.map +0 -1
- package/dist/server/tools/clast-comment.js +0 -64
- package/dist/server/tools/clast-comment.js.map +0 -1
- package/dist/server/tools/clast-file-summary.d.ts +0 -4
- package/dist/server/tools/clast-file-summary.d.ts.map +0 -1
- package/dist/server/tools/clast-file-summary.js +0 -42
- package/dist/server/tools/clast-file-summary.js.map +0 -1
- package/dist/server/tools/clast-get-context.d.ts +0 -4
- package/dist/server/tools/clast-get-context.d.ts.map +0 -1
- package/dist/server/tools/clast-get-context.js +0 -80
- package/dist/server/tools/clast-get-context.js.map +0 -1
- package/dist/server/tools/clast-reindex.d.ts +0 -5
- package/dist/server/tools/clast-reindex.d.ts.map +0 -1
- package/dist/server/tools/clast-reindex.js +0 -36
- package/dist/server/tools/clast-reindex.js.map +0 -1
- package/dist/server/tools/clast-search.d.ts +0 -4
- package/dist/server/tools/clast-search.d.ts.map +0 -1
- package/dist/server/tools/clast-search.js +0 -29
- package/dist/server/tools/clast-search.js.map +0 -1
- package/dist/server/tools/clast-status.d.ts +0 -5
- package/dist/server/tools/clast-status.d.ts.map +0 -1
- package/dist/server/tools/clast-status.js +0 -23
- package/dist/server/tools/clast-status.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/watcher/gitignore.d.ts +0 -9
- package/dist/watcher/gitignore.d.ts.map +0 -1
- package/dist/watcher/gitignore.js +0 -38
- package/dist/watcher/gitignore.js.map +0 -1
- package/dist/watcher/index.d.ts.map +0 -1
- package/skills/clast-explore/SKILL.md +0 -41
- package/src/db/queries.ts +0 -332
- package/src/db/schema.ts +0 -106
- package/src/db/types.ts +0 -47
- package/src/parser/call-resolver.ts +0 -17
- package/src/parser/languages.ts +0 -86
- package/src/server/register-tools.ts +0 -30
- package/src/server/tools/clast-call-graph.ts +0 -82
- package/src/server/tools/clast-comment.ts +0 -76
- package/src/server/tools/clast-file-summary.ts +0 -59
- package/src/server/tools/clast-get-context.ts +0 -97
- package/src/server/tools/clast-reindex.ts +0 -52
- package/src/server/tools/clast-search.ts +0 -38
- package/src/server/tools/clast-status.ts +0 -37
- package/src/watcher/gitignore.ts +0 -42
|
@@ -0,0 +1,931 @@
|
|
|
1
|
+
import { cleanComment } from '../extractor.js';
|
|
2
|
+
import { field, simpleReceiver } from '../spec.js';
|
|
3
|
+
const MAX_SIG_SLICE = 600;
|
|
4
|
+
const MAX_DOC = 600;
|
|
5
|
+
const MAX_DOC_LINES = 40;
|
|
6
|
+
const MAX_TYPE_TEXT = 80;
|
|
7
|
+
/** Prelude enum constructors: never user symbols, extremely frequent. */
|
|
8
|
+
const NOISE_CALLS = new Set(['Some', 'Ok', 'Err']);
|
|
9
|
+
/** Smart pointers / cells: `Arc::new(Foo::new())` is used like a `Foo`. */
|
|
10
|
+
const VALUE_WRAPPERS = new Set(['Arc', 'Rc', 'Box', 'Mutex', 'RwLock', 'RefCell', 'Cell', 'Pin']);
|
|
11
|
+
const states = new WeakMap();
|
|
12
|
+
function state(ctx) {
|
|
13
|
+
let s = states.get(ctx);
|
|
14
|
+
if (!s)
|
|
15
|
+
states.set(ctx, (s = { types: new Map(), attrEnd: -1, macroDefEnd: -1, pending: new Map(), implName: null, implEnd: -1 }));
|
|
16
|
+
return s;
|
|
17
|
+
}
|
|
18
|
+
function isComment(node) {
|
|
19
|
+
return node.type === 'line_comment' || node.type === 'block_comment';
|
|
20
|
+
}
|
|
21
|
+
function isPub(node) {
|
|
22
|
+
return node.firstChild?.type === 'visibility_modifier';
|
|
23
|
+
}
|
|
24
|
+
function joinDoc(parts) {
|
|
25
|
+
if (parts.length === 0)
|
|
26
|
+
return undefined;
|
|
27
|
+
// Long docs: the beginning is the summary, keep that.
|
|
28
|
+
const doc = parts.slice(0, MAX_DOC_LINES).map(c => cleanComment(c.text)).join('\n').trim();
|
|
29
|
+
return doc.length > MAX_DOC ? doc.slice(0, MAX_DOC - 3) + '...' : doc;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Doc comment of an item. The framework only looks at comment siblings
|
|
33
|
+
* directly in front of the item, so attributes between doc and item
|
|
34
|
+
* (`/// doc` + `#[derive(..)]` + item) hide it, and for long `///` blocks
|
|
35
|
+
* (one node per line) it keeps the end instead of the beginning. Returns
|
|
36
|
+
* undefined when there is no comment.
|
|
37
|
+
*/
|
|
38
|
+
function leadDoc(node) {
|
|
39
|
+
let prev = node.previousSibling;
|
|
40
|
+
if (!prev || (prev.type !== 'attribute_item' && !isComment(prev)))
|
|
41
|
+
return undefined;
|
|
42
|
+
const parts = [];
|
|
43
|
+
let nextRow = node.startPosition.row;
|
|
44
|
+
for (let i = 0; prev && i < 400; i++) {
|
|
45
|
+
if (prev.type === 'attribute_item') {
|
|
46
|
+
nextRow = prev.startPosition.row;
|
|
47
|
+
}
|
|
48
|
+
else if (isComment(prev)) {
|
|
49
|
+
// Line doc comments include their newline: they end at column 0 of the next row.
|
|
50
|
+
const end = prev.endPosition;
|
|
51
|
+
if ((end.column === 0 ? end.row - 1 : end.row) < nextRow - 1)
|
|
52
|
+
break;
|
|
53
|
+
const before = prev.previousSibling;
|
|
54
|
+
if (before && !isComment(before) && before.endPosition.row === prev.startPosition.row)
|
|
55
|
+
break;
|
|
56
|
+
parts.unshift(prev);
|
|
57
|
+
nextRow = prev.startPosition.row;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
prev = prev.previousSibling;
|
|
63
|
+
}
|
|
64
|
+
return joinDoc(parts);
|
|
65
|
+
}
|
|
66
|
+
/** True if the item is preceded by an attribute whose name is `name` (e.g. `macro_export`). */
|
|
67
|
+
function hasAttr(node, name) {
|
|
68
|
+
let prev = node.previousSibling;
|
|
69
|
+
for (let i = 0; prev && i < 20; i++) {
|
|
70
|
+
if (prev.type === 'attribute_item') {
|
|
71
|
+
if (prev.namedChildren[0]?.namedChildren[0]?.text === name)
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
else if (!isComment(prev)) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
prev = prev.previousSibling;
|
|
78
|
+
}
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
/** Source text of an item up to `end`, without trailing `;` / `,` / whitespace. */
|
|
82
|
+
function itemText(node, ctx, end = node.endIndex) {
|
|
83
|
+
const start = node.startIndex;
|
|
84
|
+
return ctx.src.slice(start, Math.min(end, start + MAX_SIG_SLICE)).replace(/[;,\s]+$/, '');
|
|
85
|
+
}
|
|
86
|
+
/** Bare name of a type / path node: `S<T>` -> S, `a::b::C` -> C, `&'a mut Foo` -> Foo. */
|
|
87
|
+
function typeName(node) {
|
|
88
|
+
for (let i = 0; node && i < 6; i++) {
|
|
89
|
+
switch (node.type) {
|
|
90
|
+
case 'type_identifier':
|
|
91
|
+
case 'identifier':
|
|
92
|
+
case 'primitive_type':
|
|
93
|
+
return node.text;
|
|
94
|
+
case 'scoped_type_identifier':
|
|
95
|
+
case 'scoped_identifier':
|
|
96
|
+
node = field(node, 'name');
|
|
97
|
+
break;
|
|
98
|
+
case 'generic_type':
|
|
99
|
+
case 'generic_type_with_turbofish':
|
|
100
|
+
case 'reference_type':
|
|
101
|
+
case 'pointer_type':
|
|
102
|
+
node = field(node, 'type');
|
|
103
|
+
break;
|
|
104
|
+
case 'array_type':
|
|
105
|
+
node = field(node, 'element');
|
|
106
|
+
break;
|
|
107
|
+
case 'dynamic_type':
|
|
108
|
+
case 'abstract_type':
|
|
109
|
+
node = field(node, 'trait');
|
|
110
|
+
break;
|
|
111
|
+
default:
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Raw type text prepared for ctx.typeHint: references, raw pointers and
|
|
119
|
+
* `mut` / `dyn` / `impl` are dropped (`&'a mut Foo` -> Foo); `Self` becomes
|
|
120
|
+
* the type of the enclosing `impl` block.
|
|
121
|
+
*/
|
|
122
|
+
function cleanType(raw, at, st) {
|
|
123
|
+
const t = raw.replace(/[&*]\s*(?:'\w+\s*)?/g, '').replace(/\b(?:mut|dyn|impl|const)\s+/g, '').trim();
|
|
124
|
+
if (t === 'Self')
|
|
125
|
+
return at < st.implEnd ? st.implName : null;
|
|
126
|
+
return t || null;
|
|
127
|
+
}
|
|
128
|
+
function typeText(node, st) {
|
|
129
|
+
if (!node || node.endIndex - node.startIndex > MAX_TYPE_TEXT)
|
|
130
|
+
return null;
|
|
131
|
+
return cleanType(node.text, node.startIndex, st);
|
|
132
|
+
}
|
|
133
|
+
function isTypeName(name) {
|
|
134
|
+
return !!name && name.length > 1 && name[0] >= 'A' && name[0] <= 'Z';
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Type of the value an expression obviously constructs: `Foo { .. }`,
|
|
138
|
+
* `Foo::new(..)` / `Foo::default()` / `m::Foo::with_capacity(..)` (a path
|
|
139
|
+
* whose type segment is capitalised; enum variants `E::V(..)` yield the enum),
|
|
140
|
+
* tuple structs `Foo(..)`, also behind `?`, `.await`, `&` and smart pointer
|
|
141
|
+
* constructors (`Arc::new(Foo::new())`). Other calls are not guessed.
|
|
142
|
+
*/
|
|
143
|
+
function valueType(v, st) {
|
|
144
|
+
for (let i = 0; v && i < 6; i++) {
|
|
145
|
+
switch (v.type) {
|
|
146
|
+
case 'try_expression':
|
|
147
|
+
case 'await_expression':
|
|
148
|
+
case 'parenthesized_expression':
|
|
149
|
+
v = v.namedChildren[0];
|
|
150
|
+
break;
|
|
151
|
+
case 'reference_expression':
|
|
152
|
+
v = field(v, 'value');
|
|
153
|
+
break;
|
|
154
|
+
case 'struct_expression': {
|
|
155
|
+
const name = typeName(field(v, 'name'));
|
|
156
|
+
if (name === 'Self')
|
|
157
|
+
return v.startIndex < st.implEnd ? st.implName : null;
|
|
158
|
+
return isTypeName(name) ? name : null;
|
|
159
|
+
}
|
|
160
|
+
case 'call_expression': {
|
|
161
|
+
let fn = field(v, 'function');
|
|
162
|
+
if (fn && fn.type === 'generic_function')
|
|
163
|
+
fn = field(fn, 'function');
|
|
164
|
+
if (!fn)
|
|
165
|
+
return null;
|
|
166
|
+
if (fn.type === 'identifier') {
|
|
167
|
+
const name = fn.text;
|
|
168
|
+
return isTypeName(name) && !NOISE_CALLS.has(name) ? name : null;
|
|
169
|
+
}
|
|
170
|
+
if (fn.type !== 'scoped_identifier')
|
|
171
|
+
return null;
|
|
172
|
+
const type = typeName(field(fn, 'path'));
|
|
173
|
+
if (type === 'Self')
|
|
174
|
+
return v.startIndex < st.implEnd ? st.implName : null;
|
|
175
|
+
if (!isTypeName(type)) {
|
|
176
|
+
// `module::Foo(..)`: tuple struct behind a module path.
|
|
177
|
+
const name = field(fn, 'name')?.text ?? null;
|
|
178
|
+
return type && isTypeName(name) && !NOISE_CALLS.has(name) ? name : null;
|
|
179
|
+
}
|
|
180
|
+
if (!VALUE_WRAPPERS.has(type))
|
|
181
|
+
return type;
|
|
182
|
+
const ctor = field(fn, 'name')?.text;
|
|
183
|
+
if (ctor !== 'new' && ctor !== 'pin')
|
|
184
|
+
return null;
|
|
185
|
+
v = field(v, 'arguments')?.namedChildren[0];
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
default:
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
/** `impl_item` / `trait_item` that directly contains the member, if any. */
|
|
195
|
+
function container(node) {
|
|
196
|
+
const list = node.parent;
|
|
197
|
+
if (!list || list.type !== 'declaration_list')
|
|
198
|
+
return null;
|
|
199
|
+
const owner = list.parent;
|
|
200
|
+
return owner && (owner.type === 'impl_item' || owner.type === 'trait_item') ? owner : null;
|
|
201
|
+
}
|
|
202
|
+
/** Visibility of a member: own `pub`, or the visibility of the trait that declares it. */
|
|
203
|
+
function memberExported(node, owner) {
|
|
204
|
+
if (isPub(node))
|
|
205
|
+
return true;
|
|
206
|
+
return !!owner && owner.type === 'trait_item' && isPub(owner);
|
|
207
|
+
}
|
|
208
|
+
function define(node, ctx, opts) {
|
|
209
|
+
const doc = leadDoc(node);
|
|
210
|
+
if (doc !== undefined)
|
|
211
|
+
opts.doc = doc;
|
|
212
|
+
return ctx.def(node, opts);
|
|
213
|
+
}
|
|
214
|
+
const functionItem = (node, ctx) => {
|
|
215
|
+
const name = field(node, 'name');
|
|
216
|
+
if (!name)
|
|
217
|
+
return;
|
|
218
|
+
const owner = container(node);
|
|
219
|
+
// Without a body (trait methods, extern functions) the signature is the whole item.
|
|
220
|
+
define(node, ctx, {
|
|
221
|
+
name: name.text,
|
|
222
|
+
kind: owner ? 'method' : 'function',
|
|
223
|
+
sig: itemText(node, ctx, field(node, 'body')?.startIndex),
|
|
224
|
+
exported: memberExported(node, owner),
|
|
225
|
+
});
|
|
226
|
+
};
|
|
227
|
+
function fields(list, ctx) {
|
|
228
|
+
if (!list || list.type !== 'field_declaration_list')
|
|
229
|
+
return;
|
|
230
|
+
for (const f of list.namedChildren) {
|
|
231
|
+
if (!f || f.type !== 'field_declaration')
|
|
232
|
+
continue;
|
|
233
|
+
const name = field(f, 'name');
|
|
234
|
+
if (!name)
|
|
235
|
+
continue;
|
|
236
|
+
define(f, ctx, { name: name.text, kind: 'field', body: null, scope: false, exported: isPub(f) });
|
|
237
|
+
ctx.typeHint(name.text, typeText(field(f, 'type'), state(ctx)));
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
const structItem = (node, ctx) => {
|
|
241
|
+
const name = field(node, 'name');
|
|
242
|
+
if (!name)
|
|
243
|
+
return;
|
|
244
|
+
const body = field(node, 'body');
|
|
245
|
+
const opts = { name: name.text, kind: 'struct', exported: isPub(node) };
|
|
246
|
+
// Unit and tuple structs: keep the whole declaration (`pub struct Id(pub u32)`).
|
|
247
|
+
if (!body || body.type !== 'field_declaration_list')
|
|
248
|
+
opts.sig = itemText(node, ctx);
|
|
249
|
+
const sym = define(node, ctx, opts);
|
|
250
|
+
state(ctx).types.set(name.text, sym);
|
|
251
|
+
fields(body, ctx);
|
|
252
|
+
};
|
|
253
|
+
const enumItem = (node, ctx) => {
|
|
254
|
+
const name = field(node, 'name');
|
|
255
|
+
if (!name)
|
|
256
|
+
return;
|
|
257
|
+
const exported = isPub(node);
|
|
258
|
+
const sym = define(node, ctx, { name: name.text, kind: 'enum', exported });
|
|
259
|
+
state(ctx).types.set(name.text, sym);
|
|
260
|
+
for (const v of field(node, 'body')?.namedChildren ?? []) {
|
|
261
|
+
if (!v || v.type !== 'enum_variant')
|
|
262
|
+
continue;
|
|
263
|
+
const vName = field(v, 'name');
|
|
264
|
+
if (!vName)
|
|
265
|
+
continue;
|
|
266
|
+
// Struct-like variants: cut at the field list (it may contain doc comments).
|
|
267
|
+
const vBody = field(v, 'body');
|
|
268
|
+
const end = vBody && vBody.type === 'field_declaration_list' ? vBody.startIndex : v.endIndex;
|
|
269
|
+
define(v, ctx, { name: vName.text, kind: 'constant', sig: itemText(v, ctx, end), scope: false, exported });
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
const traitItem = (node, ctx) => {
|
|
273
|
+
const name = field(node, 'name');
|
|
274
|
+
if (!name)
|
|
275
|
+
return;
|
|
276
|
+
const sym = define(node, ctx, { name: name.text, kind: 'trait', exported: isPub(node) });
|
|
277
|
+
for (const b of field(node, 'bounds')?.namedChildren ?? []) {
|
|
278
|
+
if (!b || b.type === 'lifetime')
|
|
279
|
+
continue;
|
|
280
|
+
ctx.inherit(sym, heritageName(b), 'extends');
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
const SMART_POINTERS = new Set(['Arc', 'Rc', 'Box', 'Pin']);
|
|
284
|
+
/** Supertype name with its module path (`futures_io::AsyncRead`), so that it is not taken for a local trait. */
|
|
285
|
+
function heritageName(node) {
|
|
286
|
+
for (let i = 0; node && i < 3 && (node.type === 'generic_type' || node.type === 'higher_ranked_trait_bound'); i++) {
|
|
287
|
+
node = field(node, 'type');
|
|
288
|
+
}
|
|
289
|
+
if (node && node.type === 'scoped_type_identifier')
|
|
290
|
+
return node.text.replace(/\s+/g, '');
|
|
291
|
+
return typeName(node);
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* The type an impl block is for. `impl Schedule for Arc<Handle>` adds methods
|
|
295
|
+
* that are called on handles, so the wrapper is looked through; a blanket
|
|
296
|
+
* `impl<T: Read> Read for Box<T>` stays with the wrapper.
|
|
297
|
+
*/
|
|
298
|
+
function implTarget(node) {
|
|
299
|
+
let type = field(node, 'type');
|
|
300
|
+
const params = new Set();
|
|
301
|
+
for (const p of field(node, 'type_parameters')?.namedChildren ?? []) {
|
|
302
|
+
const n = p ? (field(p, 'name') ?? (p.type === 'type_identifier' ? p : p.namedChildren[0])) : null;
|
|
303
|
+
if (n)
|
|
304
|
+
params.add(n.text);
|
|
305
|
+
}
|
|
306
|
+
for (let i = 0; type && i < 4; i++) {
|
|
307
|
+
if (type.type === 'reference_type') {
|
|
308
|
+
type = field(type, 'type');
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
if (type.type !== 'generic_type')
|
|
312
|
+
break;
|
|
313
|
+
const base = typeName(field(type, 'type'));
|
|
314
|
+
if (!base || !SMART_POINTERS.has(base))
|
|
315
|
+
break;
|
|
316
|
+
const inner = field(type, 'type_arguments')?.namedChildren.find(c => c && c.type !== 'lifetime') ?? null;
|
|
317
|
+
const innerName = typeName(inner);
|
|
318
|
+
if (!inner || !innerName || params.has(innerName) || innerName === 'Self')
|
|
319
|
+
break;
|
|
320
|
+
type = inner;
|
|
321
|
+
}
|
|
322
|
+
return typeName(type);
|
|
323
|
+
}
|
|
324
|
+
/** `impl Type` / `impl Trait for Type`: naming scope only, never a symbol. */
|
|
325
|
+
const implItem = (node, ctx) => {
|
|
326
|
+
const name = implTarget(node);
|
|
327
|
+
if (!name)
|
|
328
|
+
return;
|
|
329
|
+
ctx.scope(name);
|
|
330
|
+
const st = state(ctx);
|
|
331
|
+
st.implName = name;
|
|
332
|
+
st.implEnd = node.endIndex;
|
|
333
|
+
const trait = field(node, 'trait');
|
|
334
|
+
if (!trait)
|
|
335
|
+
return;
|
|
336
|
+
// Heritage needs the symbol of the implementing type: only known when the
|
|
337
|
+
// type is defined earlier in the same file.
|
|
338
|
+
const sym = st.types.get(name);
|
|
339
|
+
if (sym !== undefined)
|
|
340
|
+
ctx.inherit(sym, heritageName(trait), 'implements');
|
|
341
|
+
};
|
|
342
|
+
const modItem = (node, ctx) => {
|
|
343
|
+
const name = field(node, 'name');
|
|
344
|
+
if (!name)
|
|
345
|
+
return;
|
|
346
|
+
if (!field(node, 'body')) {
|
|
347
|
+
// `mod foo;` pulls in foo.rs / foo/mod.rs
|
|
348
|
+
ctx.imp('self::' + name.text, node, [name.text]);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
define(node, ctx, { name: name.text, kind: 'namespace', exported: isPub(node) });
|
|
352
|
+
};
|
|
353
|
+
/** `const` / `static`: module level and associated constants, not locals. */
|
|
354
|
+
const constItem = (node, ctx) => {
|
|
355
|
+
const name = field(node, 'name');
|
|
356
|
+
if (!name || ctx.inCallable())
|
|
357
|
+
return;
|
|
358
|
+
const value = field(node, 'value');
|
|
359
|
+
// Keep short values in the signature, cut long initialisers.
|
|
360
|
+
const long = value && (value.endIndex - value.startIndex > 80 || value.startPosition.row !== value.endPosition.row);
|
|
361
|
+
define(node, ctx, {
|
|
362
|
+
name: name.text,
|
|
363
|
+
kind: 'constant',
|
|
364
|
+
sig: itemText(node, ctx, long ? value.startIndex : node.endIndex).replace(/\s*=$/, ''),
|
|
365
|
+
scope: false,
|
|
366
|
+
exported: memberExported(node, container(node)),
|
|
367
|
+
});
|
|
368
|
+
};
|
|
369
|
+
const typeItem = (node, ctx) => {
|
|
370
|
+
const name = field(node, 'name');
|
|
371
|
+
if (!name)
|
|
372
|
+
return;
|
|
373
|
+
const owner = container(node);
|
|
374
|
+
const sym = define(node, ctx, {
|
|
375
|
+
name: name.text,
|
|
376
|
+
kind: 'type',
|
|
377
|
+
sig: itemText(node, ctx),
|
|
378
|
+
scope: false,
|
|
379
|
+
exported: memberExported(node, owner),
|
|
380
|
+
});
|
|
381
|
+
if (!owner)
|
|
382
|
+
state(ctx).types.set(name.text, sym);
|
|
383
|
+
};
|
|
384
|
+
const macroDefinition = (node, ctx) => {
|
|
385
|
+
const name = field(node, 'name');
|
|
386
|
+
if (!name)
|
|
387
|
+
return;
|
|
388
|
+
state(ctx).macroDefEnd = node.endIndex;
|
|
389
|
+
define(node, ctx, {
|
|
390
|
+
name: name.text,
|
|
391
|
+
kind: 'macro',
|
|
392
|
+
sig: 'macro_rules! ' + name.text,
|
|
393
|
+
exported: hasAttr(node, 'macro_export'),
|
|
394
|
+
});
|
|
395
|
+
};
|
|
396
|
+
function recordCall(fn, node, ctx) {
|
|
397
|
+
for (let i = 0; fn && i < 3; i++) {
|
|
398
|
+
switch (fn.type) {
|
|
399
|
+
case 'identifier': {
|
|
400
|
+
const name = fn.text;
|
|
401
|
+
if (!NOISE_CALLS.has(name))
|
|
402
|
+
ctx.call(name, node);
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
case 'scoped_identifier': {
|
|
406
|
+
const path = field(fn, 'path');
|
|
407
|
+
// `Vec::<u8>::new` -> receiver `Vec`
|
|
408
|
+
const recv = path && (path.type === 'generic_type' || path.type === 'bracketed_type')
|
|
409
|
+
? typeName(path)
|
|
410
|
+
: simpleReceiver(path);
|
|
411
|
+
ctx.call(field(fn, 'name')?.text, node, recv ?? '?');
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
case 'field_expression':
|
|
415
|
+
// Always a receiver ('?' for method chains): a method call is never a bare call.
|
|
416
|
+
ctx.call(field(fn, 'field')?.text, node, simpleReceiver(field(fn, 'value')) ?? '?');
|
|
417
|
+
return;
|
|
418
|
+
case 'generic_function': // `foo::<T>()`, `x.collect::<Vec<_>>()`
|
|
419
|
+
fn = field(fn, 'function');
|
|
420
|
+
break;
|
|
421
|
+
default:
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
const callExpression = (node, ctx) => {
|
|
427
|
+
recordCall(field(node, 'function'), node, ctx);
|
|
428
|
+
};
|
|
429
|
+
const macroInvocation = (node, ctx) => {
|
|
430
|
+
const m = field(node, 'macro');
|
|
431
|
+
if (!m)
|
|
432
|
+
return;
|
|
433
|
+
if (m.type === 'identifier')
|
|
434
|
+
ctx.call(m.text, node);
|
|
435
|
+
else if (m.type === 'scoped_identifier')
|
|
436
|
+
ctx.call(field(m, 'name')?.text, node, simpleReceiver(field(m, 'path')) ?? '?');
|
|
437
|
+
};
|
|
438
|
+
/** `x: &Foo`, `mut x: Foo` in function and closure parameter lists. */
|
|
439
|
+
const parameter = (node, ctx) => {
|
|
440
|
+
const list = node.parent;
|
|
441
|
+
if (!list)
|
|
442
|
+
return;
|
|
443
|
+
if (list.type !== 'closure_parameters') {
|
|
444
|
+
const owner = list.parent?.type;
|
|
445
|
+
if (owner !== 'function_item' && owner !== 'function_signature_item')
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
const pat = field(node, 'pattern');
|
|
449
|
+
if (!pat || pat.type !== 'identifier')
|
|
450
|
+
return;
|
|
451
|
+
const type = typeText(field(node, 'type'), state(ctx));
|
|
452
|
+
if (type)
|
|
453
|
+
ctx.typeHint(pat.text, type);
|
|
454
|
+
};
|
|
455
|
+
/** `let x: Foo = ..`, `let x = Foo::new(..)`, `let x = Foo { .. }` */
|
|
456
|
+
const letDeclaration = (node, ctx) => {
|
|
457
|
+
const pat = field(node, 'pattern');
|
|
458
|
+
if (!pat || pat.type !== 'identifier')
|
|
459
|
+
return;
|
|
460
|
+
const st = state(ctx);
|
|
461
|
+
const declared = field(node, 'type');
|
|
462
|
+
const type = declared ? typeText(declared, st) : valueType(field(node, 'value'), st);
|
|
463
|
+
if (type)
|
|
464
|
+
ctx.typeHint(pat.text, type);
|
|
465
|
+
};
|
|
466
|
+
/** `Foo { a, b }` uses the type. */
|
|
467
|
+
const structExpression = (node, ctx) => {
|
|
468
|
+
const name = typeName(field(node, 'name'));
|
|
469
|
+
if (name && name !== 'Self')
|
|
470
|
+
ctx.call(name, node);
|
|
471
|
+
};
|
|
472
|
+
const attributeItem = (node, ctx) => {
|
|
473
|
+
state(ctx).attrEnd = node.endIndex;
|
|
474
|
+
};
|
|
475
|
+
// ---------------------------------------------------------------------------
|
|
476
|
+
// Macro arguments (token trees)
|
|
477
|
+
// ---------------------------------------------------------------------------
|
|
478
|
+
const ITEM_KEYWORDS = new Set(['fn', 'struct', 'enum', 'union', 'trait', 'mod', 'impl', 'const', 'static', 'type']);
|
|
479
|
+
const ITEM_MODIFIERS = new Set(['pub', 'async', 'unsafe', 'const', 'extern', 'default', 'string_literal']);
|
|
480
|
+
const ITEM_KINDS = {
|
|
481
|
+
struct: 'struct', union: 'struct', enum: 'enum', trait: 'trait', mod: 'namespace',
|
|
482
|
+
const: 'constant', static: 'constant', type: 'type',
|
|
483
|
+
};
|
|
484
|
+
function isGroup(node, open) {
|
|
485
|
+
return !!node && node.type === 'token_tree' && node.firstChild?.type === open;
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Line range of an item recovered from macro arguments: from its first token
|
|
489
|
+
* (visibility / `async` / keyword) to its last one. There is no single node
|
|
490
|
+
* that covers it, and the framework takes the range of a definition from a
|
|
491
|
+
* node, so this builds the few node properties `Ctx.def` reads.
|
|
492
|
+
* `doc` is the last doc comment in front of the item (for the doc line).
|
|
493
|
+
*/
|
|
494
|
+
function span(first, last, doc) {
|
|
495
|
+
return {
|
|
496
|
+
startIndex: first.startIndex,
|
|
497
|
+
endIndex: last.endIndex,
|
|
498
|
+
startPosition: first.startPosition,
|
|
499
|
+
endPosition: last.endPosition,
|
|
500
|
+
previousSibling: doc ?? null,
|
|
501
|
+
parent: null,
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
/** Doc comments in front of token `cs[s]`, skipping `#[attr]` pairs. */
|
|
505
|
+
function docBefore(cs, s) {
|
|
506
|
+
const parts = [];
|
|
507
|
+
for (let d = s - 1; d >= 0 && parts.length < 400;) {
|
|
508
|
+
const t = cs[d];
|
|
509
|
+
if (isComment(t)) {
|
|
510
|
+
parts.unshift(t);
|
|
511
|
+
d--;
|
|
512
|
+
}
|
|
513
|
+
else if (isGroup(t, '[') && d >= 1 && cs[d - 1].type === '#') {
|
|
514
|
+
d -= 2;
|
|
515
|
+
}
|
|
516
|
+
else {
|
|
517
|
+
break;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
return parts;
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Declared types in a parameter list or struct body given as a token group:
|
|
524
|
+
* `(future: F, meta: SpawnMeta<'_>)`, `{ #[pin] pub inner: Inner, .. }`.
|
|
525
|
+
*/
|
|
526
|
+
function groupHints(group, ctx, st) {
|
|
527
|
+
const out = [];
|
|
528
|
+
const len = group.endIndex - group.startIndex;
|
|
529
|
+
if (len < 5 || len > 4000)
|
|
530
|
+
return out;
|
|
531
|
+
const text = ctx.src.slice(group.startIndex + 1, group.endIndex - 1).replace(/\/\/[^\n]*/g, '');
|
|
532
|
+
const entry = (start, end) => {
|
|
533
|
+
const m = /^\s*(?:#\[[^\]]*\]\s*)*(?:pub(?:\s*\([^)]*\))?\s+)?(?:mut\s+)?([A-Za-z_]\w*)\s*:(?!:)\s*(\S[^]*)$/.exec(text.slice(start, end));
|
|
534
|
+
if (!m || m[2].length > MAX_TYPE_TEXT)
|
|
535
|
+
return;
|
|
536
|
+
const type = cleanType(m[2], group.startIndex, st);
|
|
537
|
+
if (type)
|
|
538
|
+
out.push([m[1], type]);
|
|
539
|
+
};
|
|
540
|
+
let depth = 0;
|
|
541
|
+
let start = 0;
|
|
542
|
+
for (let i = 0; i < text.length; i++) {
|
|
543
|
+
const ch = text.charCodeAt(i);
|
|
544
|
+
if (ch === 60 || ch === 40 || ch === 91 || ch === 123)
|
|
545
|
+
depth++; // < ( [ {
|
|
546
|
+
else if (ch === 41 || ch === 93 || ch === 125)
|
|
547
|
+
depth--; // ) ] }
|
|
548
|
+
else if (ch === 62) { // > but not -> / =>
|
|
549
|
+
const before = text.charCodeAt(i - 1);
|
|
550
|
+
if (before !== 45 && before !== 61)
|
|
551
|
+
depth--;
|
|
552
|
+
}
|
|
553
|
+
else if (ch === 44 && depth === 0) { // ,
|
|
554
|
+
entry(start, i);
|
|
555
|
+
start = i + 1;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
entry(start, text.length);
|
|
559
|
+
return out;
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* `macro_rules! name { .. }` inside macro arguments (`doc! { macro_rules! select { .. } }`).
|
|
563
|
+
* `cs[i]` is the `macro_rules` identifier.
|
|
564
|
+
*/
|
|
565
|
+
function macroRules(cs, i, st) {
|
|
566
|
+
const name = cs[i + 2];
|
|
567
|
+
const body = cs[i + 3];
|
|
568
|
+
if (cs[i + 1]?.type !== '!' || !name || name.type !== 'identifier' || !body || body.type !== 'token_tree')
|
|
569
|
+
return;
|
|
570
|
+
if (st.pending.has(body.startIndex))
|
|
571
|
+
return;
|
|
572
|
+
let exported = false;
|
|
573
|
+
for (let d = i - 1; d >= 1; d--) {
|
|
574
|
+
const t = cs[d];
|
|
575
|
+
if (isComment(t))
|
|
576
|
+
continue;
|
|
577
|
+
if (!isGroup(t, '[') || cs[d - 1].type !== '#')
|
|
578
|
+
break;
|
|
579
|
+
if (t.endIndex - t.startIndex < 40 && t.text.includes('macro_export'))
|
|
580
|
+
exported = true;
|
|
581
|
+
d--;
|
|
582
|
+
}
|
|
583
|
+
const docParts = docBefore(cs, i);
|
|
584
|
+
st.pending.set(body.startIndex, {
|
|
585
|
+
def: {
|
|
586
|
+
name: name.text,
|
|
587
|
+
kind: 'macro',
|
|
588
|
+
sig: 'macro_rules! ' + name.text,
|
|
589
|
+
doc: joinDoc(docParts) ?? '',
|
|
590
|
+
exported,
|
|
591
|
+
range: span(cs[i], body, docParts[docParts.length - 1]),
|
|
592
|
+
},
|
|
593
|
+
macroDef: true,
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Receiver of a call recovered from tokens; `cs[k]` is the called name.
|
|
598
|
+
* `x.f(..)` -> x, `self.q.f(..)` -> self.q, `A::B::f(..)` -> A::B, call
|
|
599
|
+
* chains and other expressions -> '?', a bare `f(..)` -> null.
|
|
600
|
+
*/
|
|
601
|
+
function tokenReceiver(cs, k) {
|
|
602
|
+
const sep = cs[k - 1]?.type;
|
|
603
|
+
if (sep !== '.' && sep !== '::')
|
|
604
|
+
return null;
|
|
605
|
+
let recv = '';
|
|
606
|
+
for (let j = k - 2, parts = 0; j >= 0; j -= 2, parts++) {
|
|
607
|
+
const t = cs[j];
|
|
608
|
+
const type = t.type;
|
|
609
|
+
if (type !== 'identifier' && type !== 'self' && type !== 'crate' && type !== 'super')
|
|
610
|
+
return '?';
|
|
611
|
+
if (parts >= (sep === '.' ? 2 : 4))
|
|
612
|
+
return '?';
|
|
613
|
+
recv = recv ? t.text + sep + recv : t.text;
|
|
614
|
+
const before = cs[j - 1]?.type;
|
|
615
|
+
if (before === sep)
|
|
616
|
+
continue;
|
|
617
|
+
return before === '.' || before === '::' ? '?' : recv;
|
|
618
|
+
}
|
|
619
|
+
return recv || '?';
|
|
620
|
+
}
|
|
621
|
+
/** `impl<T> fmt::Debug for &'a Foo<T> where ..` -> [Foo, Debug] */
|
|
622
|
+
function implHeader(header) {
|
|
623
|
+
let h = header;
|
|
624
|
+
for (let i = 0; i < 8; i++) {
|
|
625
|
+
const next = h.replace(/<[^<>]*>/g, '');
|
|
626
|
+
if (next === h)
|
|
627
|
+
break;
|
|
628
|
+
h = next;
|
|
629
|
+
}
|
|
630
|
+
const m = /^impl\s*(?:!?\s*([\w:]+)\s+for\s+)?(?:[&*]\s*(?:'\w+\s+)?|(?:mut|const|dyn)\s+)*([\w:]+)/.exec(h);
|
|
631
|
+
if (!m)
|
|
632
|
+
return null;
|
|
633
|
+
const last = (p) => p.slice(p.lastIndexOf(':') + 1);
|
|
634
|
+
const name = last(m[2]);
|
|
635
|
+
return name ? [name, m[1] ? last(m[1]) : null] : null;
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Items inside macro arguments (`cfg_rt! { pub fn spawn() {..} }`,
|
|
639
|
+
* `pin_project! { struct X {..} }`) are plain token sequences. Recognise
|
|
640
|
+
* `<keyword> <name> ... { body }` and define the symbol when the walker
|
|
641
|
+
* reaches the body tree, so that it scopes the nested tokens like a parsed
|
|
642
|
+
* item does. `cs[i]` is the keyword token.
|
|
643
|
+
*/
|
|
644
|
+
function macroItem(cs, i, member, ctx, st) {
|
|
645
|
+
const kw = cs[i].type;
|
|
646
|
+
if (kw === 'impl') {
|
|
647
|
+
const before = i > 0 ? cs[i - 1].type : ';';
|
|
648
|
+
if (before !== ';' && before !== '{' && before !== 'token_tree' && before !== 'unsafe' && before !== 'default' &&
|
|
649
|
+
before !== 'line_comment' && before !== 'block_comment')
|
|
650
|
+
return; // `-> impl Trait`, `x: impl Trait`
|
|
651
|
+
for (let j = i + 1; j < cs.length && j < i + 80; j++) {
|
|
652
|
+
const t = cs[j];
|
|
653
|
+
if (t.type === ';')
|
|
654
|
+
return;
|
|
655
|
+
if (!isGroup(t, '{'))
|
|
656
|
+
continue;
|
|
657
|
+
if (st.pending.has(t.startIndex))
|
|
658
|
+
return;
|
|
659
|
+
const start = cs[i].startIndex;
|
|
660
|
+
const parsed = implHeader(ctx.src.slice(start, Math.min(t.startIndex, start + 400)));
|
|
661
|
+
if (!parsed)
|
|
662
|
+
return;
|
|
663
|
+
st.pending.set(t.startIndex, { scope: parsed[0], trait: parsed[1], member: true });
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
let nameNode = cs[i + 1];
|
|
669
|
+
if (nameNode && nameNode.type === 'mutable_specifier')
|
|
670
|
+
nameNode = cs[i + 2];
|
|
671
|
+
if (!nameNode || nameNode.type !== 'identifier')
|
|
672
|
+
return;
|
|
673
|
+
const name = nameNode.text;
|
|
674
|
+
// Modifiers in front of the keyword: `pub(crate) async unsafe fn`
|
|
675
|
+
let s = i;
|
|
676
|
+
let exported = false;
|
|
677
|
+
while (s > 0) {
|
|
678
|
+
const t = cs[s - 1].type;
|
|
679
|
+
if (ITEM_MODIFIERS.has(t)) {
|
|
680
|
+
if (t === 'pub')
|
|
681
|
+
exported = true;
|
|
682
|
+
s--;
|
|
683
|
+
}
|
|
684
|
+
else if (t === 'token_tree' && s >= 2 && cs[s - 2].type === 'pub') {
|
|
685
|
+
exported = true;
|
|
686
|
+
s -= 2;
|
|
687
|
+
}
|
|
688
|
+
else {
|
|
689
|
+
break;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
const docParts = docBefore(cs, s);
|
|
693
|
+
const lastDoc = docParts[docParts.length - 1];
|
|
694
|
+
const doc = joinDoc(docParts) ?? '';
|
|
695
|
+
const first = cs[s];
|
|
696
|
+
const start = first.startIndex;
|
|
697
|
+
const sigTo = (end) => ctx.src.slice(start, Math.min(end, start + MAX_SIG_SLICE)).replace(/[;,\s]+$/, '');
|
|
698
|
+
if (kw === 'const' || kw === 'static' || kw === 'type') {
|
|
699
|
+
if (kw !== 'type' && ctx.inCallable())
|
|
700
|
+
return;
|
|
701
|
+
let eq = -1;
|
|
702
|
+
for (let j = i + 2; j < cs.length && j < i + 200; j++) {
|
|
703
|
+
const t = cs[j];
|
|
704
|
+
if (t.type === '=') {
|
|
705
|
+
if (eq < 0)
|
|
706
|
+
eq = t.startIndex;
|
|
707
|
+
}
|
|
708
|
+
else if (t.type === ';') {
|
|
709
|
+
const long = eq >= 0 && t.startIndex - eq > 80;
|
|
710
|
+
ctx.def(nameNode, {
|
|
711
|
+
name, kind: ITEM_KINDS[kw], sig: sigTo(long ? eq : t.startIndex), doc, scope: false,
|
|
712
|
+
range: span(first, t, lastDoc),
|
|
713
|
+
exported: exported || (member && kw === 'type'),
|
|
714
|
+
});
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
let body = null;
|
|
721
|
+
let params = null;
|
|
722
|
+
let last = null;
|
|
723
|
+
let end = -1;
|
|
724
|
+
for (let j = i + 2; j < cs.length && j < i + 120; j++) {
|
|
725
|
+
const t = cs[j];
|
|
726
|
+
if (t.type === ';') {
|
|
727
|
+
end = t.startIndex;
|
|
728
|
+
last = t;
|
|
729
|
+
break;
|
|
730
|
+
}
|
|
731
|
+
if (isGroup(t, '{')) {
|
|
732
|
+
body = t;
|
|
733
|
+
break;
|
|
734
|
+
}
|
|
735
|
+
if (!params && kw === 'fn' && isGroup(t, '('))
|
|
736
|
+
params = t;
|
|
737
|
+
}
|
|
738
|
+
if (kw === 'mod' && !body) {
|
|
739
|
+
if (end >= 0)
|
|
740
|
+
ctx.imp('self::' + name, nameNode, [name]);
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
if (!body && end < 0)
|
|
744
|
+
return;
|
|
745
|
+
const isType = kw === 'struct' || kw === 'union' || kw === 'enum';
|
|
746
|
+
const opts = {
|
|
747
|
+
name,
|
|
748
|
+
kind: kw === 'fn' ? (member ? 'method' : 'function') : ITEM_KINDS[kw],
|
|
749
|
+
sig: sigTo(body ? body.startIndex : end),
|
|
750
|
+
doc,
|
|
751
|
+
exported,
|
|
752
|
+
// The whole item, not only its body: `pub fn spawn<F>(..) -> .. where .. { .. }`
|
|
753
|
+
range: span(first, body ?? last ?? nameNode, lastDoc),
|
|
754
|
+
};
|
|
755
|
+
if (body && !st.pending.has(body.startIndex)) {
|
|
756
|
+
const typed = kw === 'fn' ? params : kw === 'struct' || kw === 'union' ? body : null;
|
|
757
|
+
const hints = typed ? groupHints(typed, ctx, st) : undefined;
|
|
758
|
+
st.pending.set(body.startIndex, { def: opts, member: kw === 'trait', isType, hints });
|
|
759
|
+
}
|
|
760
|
+
else {
|
|
761
|
+
opts.scope = false;
|
|
762
|
+
const sym = ctx.def(nameNode, opts);
|
|
763
|
+
if (isType)
|
|
764
|
+
st.types.set(name, sym);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* Macro arguments are not parsed as expressions or items. Recover the obvious
|
|
769
|
+
* calls: an identifier directly followed by a parenthesised group (`foo(..)`,
|
|
770
|
+
* `x.bar(..)`, `A::new(..)`) or by `!` and a group (nested macros); and the
|
|
771
|
+
* items (see macroItem).
|
|
772
|
+
*/
|
|
773
|
+
const tokenTree = (node, ctx) => {
|
|
774
|
+
const st = state(ctx);
|
|
775
|
+
if (node.startIndex < st.attrEnd)
|
|
776
|
+
return;
|
|
777
|
+
if (node.firstChild?.type === '[' && node.previousSibling?.type === '#') {
|
|
778
|
+
st.attrEnd = node.endIndex; // `#[cfg(..)]` inside macro arguments / macro templates
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
let member = false;
|
|
782
|
+
const p = st.pending.get(node.startIndex);
|
|
783
|
+
if (p) {
|
|
784
|
+
st.pending.delete(node.startIndex);
|
|
785
|
+
if (p.def) {
|
|
786
|
+
const sym = ctx.def(node, p.def);
|
|
787
|
+
if (p.isType)
|
|
788
|
+
st.types.set(p.def.name, sym);
|
|
789
|
+
if (p.macroDef)
|
|
790
|
+
st.macroDefEnd = node.endIndex;
|
|
791
|
+
if (p.hints)
|
|
792
|
+
for (const [name, type] of p.hints)
|
|
793
|
+
ctx.typeHint(name, type);
|
|
794
|
+
}
|
|
795
|
+
else if (p.scope) {
|
|
796
|
+
ctx.scope(p.scope);
|
|
797
|
+
const sym = st.types.get(p.scope);
|
|
798
|
+
if (p.trait && sym !== undefined)
|
|
799
|
+
ctx.inherit(sym, p.trait, 'implements');
|
|
800
|
+
}
|
|
801
|
+
member = !!p.member;
|
|
802
|
+
}
|
|
803
|
+
const items = node.startIndex >= st.macroDefEnd;
|
|
804
|
+
const cs = node.children;
|
|
805
|
+
for (let i = 0; i < cs.length; i++) {
|
|
806
|
+
const c = cs[i];
|
|
807
|
+
if (!c)
|
|
808
|
+
continue;
|
|
809
|
+
const type = c.type;
|
|
810
|
+
if (type === 'token_tree') {
|
|
811
|
+
const prev = i > 0 ? cs[i - 1] : null;
|
|
812
|
+
if (!prev)
|
|
813
|
+
continue;
|
|
814
|
+
if (prev.type === 'identifier') {
|
|
815
|
+
// `fn name(..)` is a declaration, not a call.
|
|
816
|
+
if (c.firstChild?.type === '(' && !(i > 1 && cs[i - 2]?.type === 'fn')) {
|
|
817
|
+
const name = prev.text;
|
|
818
|
+
if (!NOISE_CALLS.has(name))
|
|
819
|
+
ctx.call(name, prev, tokenReceiver(cs, i - 1));
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
else if (prev.type === '!') {
|
|
823
|
+
const m = i > 1 ? cs[i - 2] : null;
|
|
824
|
+
if (m && m.type === 'identifier')
|
|
825
|
+
ctx.call(m.text, m);
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
else if (!items) {
|
|
829
|
+
continue;
|
|
830
|
+
}
|
|
831
|
+
else if (ITEM_KEYWORDS.has(type)) {
|
|
832
|
+
macroItem(cs, i, member, ctx, st);
|
|
833
|
+
}
|
|
834
|
+
else if (type === 'identifier' && c.endIndex - c.startIndex === 11 && c.text === 'macro_rules') {
|
|
835
|
+
macroRules(cs, i, st);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
};
|
|
839
|
+
// ---------------------------------------------------------------------------
|
|
840
|
+
// Imports
|
|
841
|
+
// ---------------------------------------------------------------------------
|
|
842
|
+
function useTree(node, prefix, decl, ctx, depth) {
|
|
843
|
+
if (!node || depth > 8)
|
|
844
|
+
return;
|
|
845
|
+
const join = (p) => (prefix ? prefix + '::' + p : p);
|
|
846
|
+
switch (node.type) {
|
|
847
|
+
case 'identifier':
|
|
848
|
+
case 'crate':
|
|
849
|
+
case 'super':
|
|
850
|
+
case 'metavariable':
|
|
851
|
+
ctx.imp(join(node.text), decl, [node.text]);
|
|
852
|
+
return;
|
|
853
|
+
case 'self': // `use a::b::{self}` imports `a::b`
|
|
854
|
+
if (prefix)
|
|
855
|
+
ctx.imp(prefix, decl, [prefix.slice(prefix.lastIndexOf(':') + 1)]);
|
|
856
|
+
return;
|
|
857
|
+
case 'scoped_identifier': {
|
|
858
|
+
const name = field(node, 'name');
|
|
859
|
+
ctx.imp(join(node.text), decl, name ? [name.text] : []);
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
case 'use_as_clause': {
|
|
863
|
+
const path = field(node, 'path');
|
|
864
|
+
const alias = field(node, 'alias');
|
|
865
|
+
if (!path)
|
|
866
|
+
return;
|
|
867
|
+
const source = path.type === 'self' && prefix ? prefix : join(path.text);
|
|
868
|
+
const original = source.slice(source.lastIndexOf('::') + 1).replace(/^:/, '');
|
|
869
|
+
// `use a::Foo as Bar`: bind the alias, remember what it stands for.
|
|
870
|
+
ctx.imp(source, decl, alias ? [alias.text !== original && /^\w+$/.test(original) ? `${alias.text}=${original}` : alias.text] : []);
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
case 'use_wildcard': {
|
|
874
|
+
const path = node.namedChildren[0];
|
|
875
|
+
ctx.imp(path ? join(path.text) + '::*' : (prefix ? prefix + '::*' : '*'), decl, ['*']);
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
case 'scoped_use_list': {
|
|
879
|
+
const path = field(node, 'path');
|
|
880
|
+
useTree(field(node, 'list'), path ? join(path.text) : prefix, decl, ctx, depth + 1);
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
case 'use_list':
|
|
884
|
+
for (const c of node.namedChildren)
|
|
885
|
+
if (c)
|
|
886
|
+
useTree(c, prefix, decl, ctx, depth + 1);
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
const useDeclaration = (node, ctx) => {
|
|
891
|
+
useTree(field(node, 'argument'), '', node, ctx, 0);
|
|
892
|
+
};
|
|
893
|
+
const externCrate = (node, ctx) => {
|
|
894
|
+
const name = field(node, 'name');
|
|
895
|
+
if (name)
|
|
896
|
+
ctx.imp(name.text, node, [field(node, 'alias')?.text ?? name.text]);
|
|
897
|
+
};
|
|
898
|
+
export const rust = {
|
|
899
|
+
id: 'rust',
|
|
900
|
+
grammar: 'rust',
|
|
901
|
+
extensions: ['.rs'],
|
|
902
|
+
packageTypes: true,
|
|
903
|
+
identifiers: ['identifier', 'type_identifier', 'field_identifier'],
|
|
904
|
+
comments: ['line_comment', 'block_comment'],
|
|
905
|
+
visit: {
|
|
906
|
+
function_item: functionItem,
|
|
907
|
+
function_signature_item: functionItem,
|
|
908
|
+
struct_item: structItem,
|
|
909
|
+
union_item: structItem,
|
|
910
|
+
enum_item: enumItem,
|
|
911
|
+
trait_item: traitItem,
|
|
912
|
+
impl_item: implItem,
|
|
913
|
+
mod_item: modItem,
|
|
914
|
+
const_item: constItem,
|
|
915
|
+
static_item: constItem,
|
|
916
|
+
type_item: typeItem,
|
|
917
|
+
associated_type: typeItem,
|
|
918
|
+
macro_definition: macroDefinition,
|
|
919
|
+
call_expression: callExpression,
|
|
920
|
+
macro_invocation: macroInvocation,
|
|
921
|
+
struct_expression: structExpression,
|
|
922
|
+
parameter: parameter,
|
|
923
|
+
let_declaration: letDeclaration,
|
|
924
|
+
attribute_item: attributeItem,
|
|
925
|
+
inner_attribute_item: attributeItem,
|
|
926
|
+
token_tree: tokenTree,
|
|
927
|
+
use_declaration: useDeclaration,
|
|
928
|
+
extern_crate_declaration: externCrate,
|
|
929
|
+
},
|
|
930
|
+
};
|
|
931
|
+
//# sourceMappingURL=rust.js.map
|