+1
.gitignore
+1
.gitignore
···
···
1
+
target/
+398
Cargo.lock
+398
Cargo.lock
···
···
1
+
# This file is automatically @generated by Cargo.
2
+
# It is not intended for manual editing.
3
+
version = 3
4
+
5
+
[[package]]
6
+
name = "ahash"
7
+
version = "0.4.8"
8
+
source = "registry+https://github.com/rust-lang/crates.io-index"
9
+
checksum = "0453232ace82dee0dd0b4c87a59bd90f7b53b314f3e0f61fe2ee7c8a16482289"
10
+
11
+
[[package]]
12
+
name = "aho-corasick"
13
+
version = "1.1.2"
14
+
source = "registry+https://github.com/rust-lang/crates.io-index"
15
+
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
16
+
dependencies = [
17
+
"memchr",
18
+
]
19
+
20
+
[[package]]
21
+
name = "cc"
22
+
version = "1.0.83"
23
+
source = "registry+https://github.com/rust-lang/crates.io-index"
24
+
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
25
+
dependencies = [
26
+
"libc",
27
+
]
28
+
29
+
[[package]]
30
+
name = "cfg-if"
31
+
version = "1.0.0"
32
+
source = "registry+https://github.com/rust-lang/crates.io-index"
33
+
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
34
+
35
+
[[package]]
36
+
name = "darling"
37
+
version = "0.20.6"
38
+
source = "registry+https://github.com/rust-lang/crates.io-index"
39
+
checksum = "c376d08ea6aa96aafe61237c7200d1241cb177b7d3a542d791f2d118e9cbb955"
40
+
dependencies = [
41
+
"darling_core",
42
+
"darling_macro",
43
+
]
44
+
45
+
[[package]]
46
+
name = "darling_core"
47
+
version = "0.20.6"
48
+
source = "registry+https://github.com/rust-lang/crates.io-index"
49
+
checksum = "33043dcd19068b8192064c704b3f83eb464f91f1ff527b44a4e2b08d9cdb8855"
50
+
dependencies = [
51
+
"fnv",
52
+
"ident_case",
53
+
"proc-macro2",
54
+
"quote",
55
+
"strsim",
56
+
"syn",
57
+
]
58
+
59
+
[[package]]
60
+
name = "darling_macro"
61
+
version = "0.20.6"
62
+
source = "registry+https://github.com/rust-lang/crates.io-index"
63
+
checksum = "c5a91391accf613803c2a9bf9abccdbaa07c54b4244a5b64883f9c3c137c86be"
64
+
dependencies = [
65
+
"darling_core",
66
+
"quote",
67
+
"syn",
68
+
]
69
+
70
+
[[package]]
71
+
name = "derive_builder"
72
+
version = "0.20.0"
73
+
source = "registry+https://github.com/rust-lang/crates.io-index"
74
+
checksum = "0350b5cb0331628a5916d6c5c0b72e97393b8b6b03b47a9284f4e7f5a405ffd7"
75
+
dependencies = [
76
+
"derive_builder_macro",
77
+
]
78
+
79
+
[[package]]
80
+
name = "derive_builder_core"
81
+
version = "0.20.0"
82
+
source = "registry+https://github.com/rust-lang/crates.io-index"
83
+
checksum = "d48cda787f839151732d396ac69e3473923d54312c070ee21e9effcaa8ca0b1d"
84
+
dependencies = [
85
+
"darling",
86
+
"proc-macro2",
87
+
"quote",
88
+
"syn",
89
+
]
90
+
91
+
[[package]]
92
+
name = "derive_builder_macro"
93
+
version = "0.20.0"
94
+
source = "registry+https://github.com/rust-lang/crates.io-index"
95
+
checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b"
96
+
dependencies = [
97
+
"derive_builder_core",
98
+
"syn",
99
+
]
100
+
101
+
[[package]]
102
+
name = "dissimilar"
103
+
version = "1.0.7"
104
+
source = "registry+https://github.com/rust-lang/crates.io-index"
105
+
checksum = "86e3bdc80eee6e16b2b6b0f87fbc98c04bee3455e35174c0de1a125d0688c632"
106
+
107
+
[[package]]
108
+
name = "equivalent"
109
+
version = "1.0.1"
110
+
source = "registry+https://github.com/rust-lang/crates.io-index"
111
+
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
112
+
113
+
[[package]]
114
+
name = "expect-test"
115
+
version = "1.4.1"
116
+
source = "registry+https://github.com/rust-lang/crates.io-index"
117
+
checksum = "30d9eafeadd538e68fb28016364c9732d78e420b9ff8853fa5e4058861e9f8d3"
118
+
dependencies = [
119
+
"dissimilar",
120
+
"once_cell",
121
+
]
122
+
123
+
[[package]]
124
+
name = "fixedbitset"
125
+
version = "0.4.2"
126
+
source = "registry+https://github.com/rust-lang/crates.io-index"
127
+
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
128
+
129
+
[[package]]
130
+
name = "fnv"
131
+
version = "1.0.7"
132
+
source = "registry+https://github.com/rust-lang/crates.io-index"
133
+
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
134
+
135
+
[[package]]
136
+
name = "hashbrown"
137
+
version = "0.9.1"
138
+
source = "registry+https://github.com/rust-lang/crates.io-index"
139
+
checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
140
+
dependencies = [
141
+
"ahash",
142
+
]
143
+
144
+
[[package]]
145
+
name = "hashbrown"
146
+
version = "0.14.3"
147
+
source = "registry+https://github.com/rust-lang/crates.io-index"
148
+
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
149
+
150
+
[[package]]
151
+
name = "ident_case"
152
+
version = "1.0.1"
153
+
source = "registry+https://github.com/rust-lang/crates.io-index"
154
+
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
155
+
156
+
[[package]]
157
+
name = "indexmap"
158
+
version = "2.2.3"
159
+
source = "registry+https://github.com/rust-lang/crates.io-index"
160
+
checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177"
161
+
dependencies = [
162
+
"equivalent",
163
+
"hashbrown 0.14.3",
164
+
]
165
+
166
+
[[package]]
167
+
name = "itoa"
168
+
version = "1.0.10"
169
+
source = "registry+https://github.com/rust-lang/crates.io-index"
170
+
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
171
+
172
+
[[package]]
173
+
name = "libc"
174
+
version = "0.2.153"
175
+
source = "registry+https://github.com/rust-lang/crates.io-index"
176
+
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
177
+
178
+
[[package]]
179
+
name = "log"
180
+
version = "0.4.20"
181
+
source = "registry+https://github.com/rust-lang/crates.io-index"
182
+
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
183
+
184
+
[[package]]
185
+
name = "memchr"
186
+
version = "2.7.1"
187
+
source = "registry+https://github.com/rust-lang/crates.io-index"
188
+
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
189
+
190
+
[[package]]
191
+
name = "once_cell"
192
+
version = "1.19.0"
193
+
source = "registry+https://github.com/rust-lang/crates.io-index"
194
+
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
195
+
196
+
[[package]]
197
+
name = "petgraph"
198
+
version = "0.6.4"
199
+
source = "registry+https://github.com/rust-lang/crates.io-index"
200
+
checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9"
201
+
dependencies = [
202
+
"fixedbitset",
203
+
"indexmap",
204
+
"serde",
205
+
"serde_derive",
206
+
]
207
+
208
+
[[package]]
209
+
name = "proc-macro2"
210
+
version = "1.0.78"
211
+
source = "registry+https://github.com/rust-lang/crates.io-index"
212
+
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
213
+
dependencies = [
214
+
"unicode-ident",
215
+
]
216
+
217
+
[[package]]
218
+
name = "quote"
219
+
version = "1.0.35"
220
+
source = "registry+https://github.com/rust-lang/crates.io-index"
221
+
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
222
+
dependencies = [
223
+
"proc-macro2",
224
+
]
225
+
226
+
[[package]]
227
+
name = "regex"
228
+
version = "1.10.3"
229
+
source = "registry+https://github.com/rust-lang/crates.io-index"
230
+
checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
231
+
dependencies = [
232
+
"aho-corasick",
233
+
"memchr",
234
+
"regex-automata",
235
+
"regex-syntax",
236
+
]
237
+
238
+
[[package]]
239
+
name = "regex-automata"
240
+
version = "0.4.5"
241
+
source = "registry+https://github.com/rust-lang/crates.io-index"
242
+
checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
243
+
dependencies = [
244
+
"aho-corasick",
245
+
"memchr",
246
+
"regex-syntax",
247
+
]
248
+
249
+
[[package]]
250
+
name = "regex-syntax"
251
+
version = "0.8.2"
252
+
source = "registry+https://github.com/rust-lang/crates.io-index"
253
+
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
254
+
255
+
[[package]]
256
+
name = "ryu"
257
+
version = "1.0.16"
258
+
source = "registry+https://github.com/rust-lang/crates.io-index"
259
+
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
260
+
261
+
[[package]]
262
+
name = "serde"
263
+
version = "1.0.196"
264
+
source = "registry+https://github.com/rust-lang/crates.io-index"
265
+
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
266
+
dependencies = [
267
+
"serde_derive",
268
+
]
269
+
270
+
[[package]]
271
+
name = "serde_derive"
272
+
version = "1.0.196"
273
+
source = "registry+https://github.com/rust-lang/crates.io-index"
274
+
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
275
+
dependencies = [
276
+
"proc-macro2",
277
+
"quote",
278
+
"syn",
279
+
]
280
+
281
+
[[package]]
282
+
name = "serde_json"
283
+
version = "1.0.113"
284
+
source = "registry+https://github.com/rust-lang/crates.io-index"
285
+
checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
286
+
dependencies = [
287
+
"itoa",
288
+
"ryu",
289
+
"serde",
290
+
]
291
+
292
+
[[package]]
293
+
name = "smallvec"
294
+
version = "1.13.1"
295
+
source = "registry+https://github.com/rust-lang/crates.io-index"
296
+
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
297
+
298
+
[[package]]
299
+
name = "stag"
300
+
version = "0.1.0"
301
+
dependencies = [
302
+
"derive_builder",
303
+
"expect-test",
304
+
"once_cell",
305
+
"petgraph",
306
+
"serde",
307
+
"thiserror",
308
+
"tree-sitter",
309
+
"tree-sitter-graph",
310
+
"tree-sitter-rust",
311
+
]
312
+
313
+
[[package]]
314
+
name = "string-interner"
315
+
version = "0.12.2"
316
+
source = "registry+https://github.com/rust-lang/crates.io-index"
317
+
checksum = "383196d1876517ee6f9f0864d1fc1070331b803335d3c6daaa04bbcccd823c08"
318
+
dependencies = [
319
+
"cfg-if",
320
+
"hashbrown 0.9.1",
321
+
]
322
+
323
+
[[package]]
324
+
name = "strsim"
325
+
version = "0.10.0"
326
+
source = "registry+https://github.com/rust-lang/crates.io-index"
327
+
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
328
+
329
+
[[package]]
330
+
name = "syn"
331
+
version = "2.0.48"
332
+
source = "registry+https://github.com/rust-lang/crates.io-index"
333
+
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
334
+
dependencies = [
335
+
"proc-macro2",
336
+
"quote",
337
+
"unicode-ident",
338
+
]
339
+
340
+
[[package]]
341
+
name = "thiserror"
342
+
version = "1.0.57"
343
+
source = "registry+https://github.com/rust-lang/crates.io-index"
344
+
checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b"
345
+
dependencies = [
346
+
"thiserror-impl",
347
+
]
348
+
349
+
[[package]]
350
+
name = "thiserror-impl"
351
+
version = "1.0.57"
352
+
source = "registry+https://github.com/rust-lang/crates.io-index"
353
+
checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
354
+
dependencies = [
355
+
"proc-macro2",
356
+
"quote",
357
+
"syn",
358
+
]
359
+
360
+
[[package]]
361
+
name = "tree-sitter"
362
+
version = "0.20.10"
363
+
source = "registry+https://github.com/rust-lang/crates.io-index"
364
+
checksum = "e747b1f9b7b931ed39a548c1fae149101497de3c1fc8d9e18c62c1a66c683d3d"
365
+
dependencies = [
366
+
"cc",
367
+
"regex",
368
+
]
369
+
370
+
[[package]]
371
+
name = "tree-sitter-graph"
372
+
version = "0.11.0"
373
+
dependencies = [
374
+
"log",
375
+
"regex",
376
+
"serde",
377
+
"serde_json",
378
+
"smallvec",
379
+
"string-interner",
380
+
"thiserror",
381
+
"tree-sitter",
382
+
]
383
+
384
+
[[package]]
385
+
name = "tree-sitter-rust"
386
+
version = "0.20.4"
387
+
source = "registry+https://github.com/rust-lang/crates.io-index"
388
+
checksum = "b0832309b0b2b6d33760ce5c0e818cb47e1d72b468516bfe4134408926fa7594"
389
+
dependencies = [
390
+
"cc",
391
+
"tree-sitter",
392
+
]
393
+
394
+
[[package]]
395
+
name = "unicode-ident"
396
+
version = "1.0.12"
397
+
source = "registry+https://github.com/rust-lang/crates.io-index"
398
+
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+20
Cargo.toml
+20
Cargo.toml
···
···
1
+
[package]
2
+
name = "stag"
3
+
version = "0.1.0"
4
+
edition = "2021"
5
+
6
+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7
+
8
+
[lib]
9
+
10
+
[dependencies]
11
+
petgraph = { version = "0.6.4", default-features = false, features = ["serde-1"] }
12
+
serde = {version = "1.0.188", features = ["derive"]}
13
+
once_cell = "1.19.0"
14
+
thiserror = "1.0.56"
15
+
tree-sitter-graph = { path = "../tree-sitter-graph/"}
16
+
tree-sitter-rust = "0.20.4"
17
+
tree-sitter = "0.20.10"
18
+
derive_builder = "0.20.0"
19
+
expect-test = "1.4.1"
20
+
+240
src/debug.rs
+240
src/debug.rs
···
···
1
+
use std::fmt;
2
+
3
+
use crate::text_range::TextRange;
4
+
use crate::{EdgeKind, LocalDef, NodeKind};
5
+
6
+
use petgraph::{
7
+
graph::{Graph, NodeIndex},
8
+
visit::EdgeRef,
9
+
Direction,
10
+
};
11
+
12
+
pub struct ScopeDebug {
13
+
range: TextRange,
14
+
defs: Vec<DefDebug>,
15
+
imports: Vec<ImportDebug>,
16
+
scopes: Vec<ScopeDebug>,
17
+
}
18
+
19
+
struct DefDebug {
20
+
name: String,
21
+
range: TextRange,
22
+
context: String,
23
+
refs: Vec<RefDebug>,
24
+
}
25
+
26
+
struct RefDebug {
27
+
context: String,
28
+
}
29
+
30
+
struct ImportDebug {
31
+
name: String,
32
+
range: TextRange,
33
+
context: String,
34
+
refs: Vec<RefDebug>,
35
+
}
36
+
37
+
impl DefDebug {
38
+
fn new(range: TextRange, name: String, refs: Vec<TextRange>, src: &[u8]) -> Self {
39
+
Self {
40
+
name,
41
+
range,
42
+
context: context(range, src),
43
+
refs: refs
44
+
.into_iter()
45
+
.map(|r| context(r, src))
46
+
.map(|context| RefDebug { context })
47
+
.collect(),
48
+
}
49
+
}
50
+
}
51
+
52
+
impl ImportDebug {
53
+
fn new(range: TextRange, name: String, refs: Vec<TextRange>, src: &[u8]) -> Self {
54
+
Self {
55
+
name,
56
+
range,
57
+
context: context(range, src),
58
+
refs: refs
59
+
.into_iter()
60
+
.map(|r| context(r, src))
61
+
.map(|context| RefDebug { context })
62
+
.collect(),
63
+
}
64
+
}
65
+
}
66
+
67
+
impl ScopeDebug {
68
+
fn empty(range: TextRange) -> Self {
69
+
Self {
70
+
range,
71
+
defs: Vec::new(),
72
+
imports: Vec::new(),
73
+
scopes: Vec::new(),
74
+
}
75
+
}
76
+
77
+
fn build(&mut self, graph: &Graph<NodeKind, EdgeKind>, start: NodeIndex<u32>, src: &[u8]) {
78
+
let mut defs = graph
79
+
.edges_directed(start, Direction::Incoming)
80
+
.filter(|edge| *edge.weight() == EdgeKind::DefToScope)
81
+
.map(|edge| {
82
+
let def_node = edge.source();
83
+
84
+
// range of this def
85
+
let range = graph[def_node].range();
86
+
87
+
// text source of this def
88
+
let text = std::str::from_utf8(&src[range.start.byte..range.end.byte])
89
+
.unwrap()
90
+
.to_owned();
91
+
92
+
// all references of this def, sorted by range
93
+
let mut refs = graph
94
+
.edges_directed(def_node, Direction::Incoming)
95
+
.filter(|edge| *edge.weight() == EdgeKind::RefToDef)
96
+
.map(|edge| graph[edge.source()].range())
97
+
.collect::<Vec<_>>();
98
+
99
+
refs.sort();
100
+
101
+
DefDebug::new(range, text, refs, src)
102
+
})
103
+
.collect::<Vec<_>>();
104
+
105
+
let mut imports = graph
106
+
.edges_directed(start, Direction::Incoming)
107
+
.filter(|edge| *edge.weight() == EdgeKind::ImportToScope)
108
+
.map(|edge| {
109
+
let imp_node = edge.source();
110
+
111
+
// range of this import
112
+
let range = graph[imp_node].range();
113
+
114
+
// text source of this import
115
+
let text = std::str::from_utf8(&src[range.start.byte..range.end.byte])
116
+
.unwrap()
117
+
.to_owned();
118
+
119
+
// all references of this import, sorted by range
120
+
let mut refs = graph
121
+
.edges_directed(imp_node, Direction::Incoming)
122
+
.filter(|edge| *edge.weight() == EdgeKind::RefToImport)
123
+
.map(|edge| graph[edge.source()].range())
124
+
.collect::<Vec<_>>();
125
+
126
+
refs.sort();
127
+
128
+
ImportDebug::new(range, text, refs, src)
129
+
})
130
+
.collect::<Vec<_>>();
131
+
132
+
let mut scopes = graph
133
+
.edges_directed(start, Direction::Incoming)
134
+
.filter(|edge| *edge.weight() == EdgeKind::ScopeToScope)
135
+
.map(|edge| {
136
+
let source_scope = edge.source();
137
+
let mut scope_debug = ScopeDebug::empty(graph[source_scope].range());
138
+
scope_debug.build(graph, source_scope, src);
139
+
scope_debug
140
+
})
141
+
.collect::<Vec<_>>();
142
+
143
+
// sort defs by their ranges
144
+
defs.sort_by(|a, b| a.range.cmp(&b.range));
145
+
// sort imports by their ranges
146
+
imports.sort_by(|a, b| a.range.cmp(&b.range));
147
+
// sort scopes by their ranges
148
+
scopes.sort_by(|a, b| a.range.cmp(&b.range));
149
+
150
+
self.defs = defs;
151
+
self.imports = imports;
152
+
self.scopes = scopes;
153
+
}
154
+
155
+
pub fn new(graph: &Graph<NodeKind, EdgeKind>, start: NodeIndex<u32>, src: &[u8]) -> Self {
156
+
let mut scope_debug = Self::empty(graph[start].range());
157
+
scope_debug.build(graph, start, src);
158
+
scope_debug
159
+
}
160
+
}
161
+
162
+
impl fmt::Debug for ScopeDebug {
163
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164
+
if self.imports.is_empty() {
165
+
f.debug_struct("scope")
166
+
.field("definitions", &self.defs)
167
+
.field("child scopes", &self.scopes)
168
+
.finish()
169
+
} else {
170
+
f.debug_struct("scope")
171
+
.field("definitions", &self.defs)
172
+
.field("imports", &self.imports)
173
+
.field("child scopes", &self.scopes)
174
+
.finish()
175
+
}
176
+
}
177
+
}
178
+
179
+
impl fmt::Debug for DefDebug {
180
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181
+
let mut s = f.debug_struct(&self.name);
182
+
let d = s.field("context", &self.context);
183
+
184
+
if self.refs.is_empty() {
185
+
d
186
+
} else {
187
+
d.field(&format!("referenced in ({})", self.refs.len()), &self.refs)
188
+
}
189
+
.finish()
190
+
}
191
+
}
192
+
193
+
impl fmt::Debug for ImportDebug {
194
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195
+
let mut s = f.debug_struct(&self.name);
196
+
let d = s.field("context", &self.context);
197
+
198
+
if self.refs.is_empty() {
199
+
d
200
+
} else {
201
+
d.field(&format!("referenced in ({})", self.refs.len()), &self.refs)
202
+
}
203
+
.finish()
204
+
}
205
+
}
206
+
207
+
impl fmt::Debug for RefDebug {
208
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
209
+
write!(f, "`{}`", self.context)
210
+
}
211
+
}
212
+
213
+
fn context(range: TextRange, src: &[u8]) -> String {
214
+
// first new line before start
215
+
let context_start = src
216
+
.iter()
217
+
.enumerate()
218
+
.take(range.start.byte)
219
+
.rev()
220
+
.find_map(|(idx, &c)| (c == b'\n').then_some(idx))
221
+
.unwrap_or(range.start.byte - 1)
222
+
.saturating_add(1);
223
+
224
+
// first new line after end
225
+
let context_end: usize = src
226
+
.iter()
227
+
.enumerate()
228
+
.skip(range.end.byte)
229
+
.find_map(|(idx, &c)| (c == b'\n').then_some(idx))
230
+
.unwrap_or(range.end.byte + 1)
231
+
.saturating_sub(1);
232
+
233
+
let from_utf8 = |bytes| std::str::from_utf8(bytes).unwrap();
234
+
format!(
235
+
"{}§{}§{}",
236
+
from_utf8(&src[context_start..range.start.byte]).trim_start(),
237
+
from_utf8(&src[range.start.byte..range.end.byte]),
238
+
from_utf8(&src[range.end.byte..=context_end]).trim_end()
239
+
)
240
+
}
+28
src/error.rs
+28
src/error.rs
···
···
1
+
use thiserror::Error;
2
+
3
+
#[derive(Error, Debug)]
4
+
pub enum StagError {
5
+
#[error("tree-sitter: {0}")]
6
+
TreeSitter(#[from] tree_sitter::LanguageError),
7
+
8
+
#[error("config error: {0}")]
9
+
Config(#[from] ConfigError),
10
+
}
11
+
12
+
#[derive(Error, Debug)]
13
+
pub enum ConfigError {
14
+
#[error("no language configured")]
15
+
MissingLanguage,
16
+
17
+
#[error("no stag query configured")]
18
+
MissingStagSource,
19
+
20
+
#[error("no source file configured")]
21
+
MissingSource,
22
+
23
+
#[error("parse error: {0}")]
24
+
Parse(#[from] tree_sitter_graph::ParseError),
25
+
26
+
#[error("tree-sitter graph execution error: {0}")]
27
+
TsgExecute(#[from] tree_sitter_graph::ExecutionError),
28
+
}
+1414
src/lib.rs
+1414
src/lib.rs
···
···
1
+
mod text_range;
2
+
use petgraph::{graph::NodeIndex, visit::EdgeRef, Direction, Graph};
3
+
use serde::{Deserialize, Serialize};
4
+
use text_range::TextRange;
5
+
use tree_sitter_graph::{
6
+
ast::File, functions::Functions, ExecutionConfig, Identifier, NoCancellation, Variables,
7
+
};
8
+
9
+
mod debug;
10
+
mod error;
11
+
mod stdlib;
12
+
13
+
use error::{ConfigError, StagError};
14
+
15
+
#[derive(Default)]
16
+
pub struct StagBuilder<'a> {
17
+
language: Option<tree_sitter::Language>,
18
+
stag_file: Option<&'a str>,
19
+
filename: Option<&'a str>,
20
+
source: Option<&'a str>,
21
+
}
22
+
23
+
impl<'a> StagBuilder<'a> {
24
+
pub fn with_language(&mut self, language: tree_sitter::Language) -> &mut Self {
25
+
self.language = Some(language);
26
+
self
27
+
}
28
+
29
+
pub fn with_stag_file(&mut self, stag_file: &'a str) -> &mut Self {
30
+
self.stag_file = Some(stag_file);
31
+
self
32
+
}
33
+
34
+
pub fn with_source(&mut self, source: &'a str) -> &mut Self {
35
+
self.source = Some(source);
36
+
self
37
+
}
38
+
39
+
pub fn with_file_name(&mut self, filename: &'a str) -> &mut Self {
40
+
self.filename = Some(filename);
41
+
self
42
+
}
43
+
44
+
pub fn execute(&self) -> Result<ScopeGraph, StagError> {
45
+
let mut parser = tree_sitter::Parser::new();
46
+
let language = self.language.ok_or(ConfigError::MissingLanguage)?;
47
+
parser.set_language(language)?;
48
+
49
+
let file = File::from_str(
50
+
language,
51
+
self.stag_file.ok_or(ConfigError::MissingStagSource)?,
52
+
)
53
+
.map_err(ConfigError::from)?;
54
+
55
+
let mut functions = Functions::stdlib();
56
+
functions.add(Identifier::from("scope"), stdlib::ScopeShorthand);
57
+
functions.add(Identifier::from("def"), stdlib::DefShorthand);
58
+
functions.add(Identifier::from("ref"), stdlib::RefShortHand);
59
+
functions.add(Identifier::from("cover"), stdlib::CoverRanges);
60
+
functions.add(Identifier::from("range"), stdlib::NodeRange);
61
+
let globals = Variables::new();
62
+
let config = ExecutionConfig::new(&functions, &globals);
63
+
64
+
let src = self.source.ok_or(ConfigError::MissingSource)?;
65
+
let tree = parser.parse(src.as_bytes(), None).unwrap();
66
+
let graph = file
67
+
.execute(&tree, &src, &config, &NoCancellation)
68
+
.map_err(ConfigError::from)?;
69
+
70
+
let mut sg = ScopeGraph::new(tree.root_node().range().into());
71
+
sg = build_scope_graph(graph, sg);
72
+
Ok(sg)
73
+
}
74
+
}
75
+
76
+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
77
+
pub struct LocalDef {
78
+
pub range: TextRange,
79
+
pub text: String,
80
+
pub symbol: Option<String>,
81
+
}
82
+
83
+
impl LocalDef {
84
+
/// Initialize a new definition
85
+
pub fn new(range: TextRange, text: String, symbol: Option<String>) -> Self {
86
+
Self {
87
+
range,
88
+
text,
89
+
symbol,
90
+
}
91
+
}
92
+
93
+
pub fn name<'a>(&self, buffer: &'a [u8]) -> &'a [u8] {
94
+
&buffer[self.range.start.byte..self.range.end.byte]
95
+
}
96
+
}
97
+
98
+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
99
+
pub struct LocalImport {
100
+
pub range: TextRange,
101
+
pub text: String,
102
+
}
103
+
104
+
impl LocalImport {
105
+
/// Initialize a new import
106
+
pub fn new(range: TextRange, text: String) -> Self {
107
+
Self { range, text }
108
+
}
109
+
110
+
pub fn name<'a>(&self, buffer: &'a [u8]) -> &'a [u8] {
111
+
&buffer[self.range.start.byte..self.range.end.byte]
112
+
}
113
+
}
114
+
115
+
#[derive(Debug, Clone, Serialize, Deserialize)]
116
+
pub struct Reference {
117
+
pub range: TextRange,
118
+
pub text: String,
119
+
pub symbol: Option<String>,
120
+
}
121
+
122
+
impl Reference {
123
+
/// Initialize a new reference
124
+
pub fn new(range: TextRange, text: String, symbol: Option<String>) -> Self {
125
+
Self {
126
+
range,
127
+
text,
128
+
symbol,
129
+
}
130
+
}
131
+
}
132
+
133
+
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
134
+
pub struct LocalScope {
135
+
pub range: TextRange,
136
+
}
137
+
138
+
impl LocalScope {
139
+
pub fn new(range: TextRange) -> Self {
140
+
Self { range }
141
+
}
142
+
}
143
+
144
+
pub struct ScopeStack<'a> {
145
+
pub scope_graph: &'a ScopeGraph,
146
+
pub start: Option<NodeIndex<u32>>,
147
+
}
148
+
149
+
impl<'a> Iterator for ScopeStack<'a> {
150
+
type Item = NodeIndex<u32>;
151
+
fn next(&mut self) -> Option<Self::Item> {
152
+
if let Some(start) = self.start {
153
+
let parent = self
154
+
.scope_graph
155
+
.graph
156
+
.edges_directed(start, Direction::Outgoing)
157
+
.find(|edge| *edge.weight() == EdgeKind::ScopeToScope)
158
+
.map(|edge| edge.target());
159
+
let original = start;
160
+
self.start = parent;
161
+
Some(original)
162
+
} else {
163
+
None
164
+
}
165
+
}
166
+
}
167
+
168
+
/// The type of a node in the ScopeGraph
169
+
#[derive(Serialize, Deserialize, Debug, Clone)]
170
+
pub enum NodeKind {
171
+
/// A scope node
172
+
Scope(LocalScope),
173
+
174
+
/// A definition node
175
+
Def(LocalDef),
176
+
177
+
/// An import node
178
+
Import(LocalImport),
179
+
180
+
/// A reference node
181
+
Ref(Reference),
182
+
}
183
+
184
+
impl NodeKind {
185
+
/// Construct a scope node from a range
186
+
pub fn scope(range: TextRange) -> Self {
187
+
Self::Scope(LocalScope::new(range))
188
+
}
189
+
190
+
/// Produce the range spanned by this node
191
+
pub fn range(&self) -> TextRange {
192
+
match self {
193
+
Self::Scope(l) => l.range,
194
+
Self::Def(d) => d.range,
195
+
Self::Ref(r) => r.range,
196
+
Self::Import(i) => i.range,
197
+
}
198
+
}
199
+
}
200
+
201
+
/// Describes the relation between two nodes in the ScopeGraph
202
+
#[derive(Serialize, Deserialize, PartialEq, Eq, Copy, Clone, Debug)]
203
+
pub enum EdgeKind {
204
+
/// The edge weight from a nested scope to its parent scope
205
+
ScopeToScope,
206
+
207
+
/// The edge weight from a definition to its definition scope
208
+
DefToScope,
209
+
210
+
/// The edge weight from an import to its definition scope
211
+
ImportToScope,
212
+
213
+
/// The edge weight from a reference to its definition
214
+
RefToDef,
215
+
216
+
/// The edge weight from a reference to its import
217
+
RefToImport,
218
+
}
219
+
220
+
/// A graph representation of scopes and names in a single syntax tree
221
+
#[derive(Debug, Serialize, Deserialize, Clone)]
222
+
pub struct ScopeGraph {
223
+
/// The raw graph
224
+
pub graph: Graph<NodeKind, EdgeKind>,
225
+
226
+
// Graphs do not have the concept of a `root`, but lexical scopes follow the syntax
227
+
// tree, and as a result, have a "root" node. The root_idx points to a scope node that
228
+
// encompasses the entire file: the file-global scope.
229
+
root_idx: NodeIndex,
230
+
}
231
+
232
+
impl ScopeGraph {
233
+
pub fn new(range: TextRange) -> Self {
234
+
let mut graph = Graph::new();
235
+
let root_idx = graph.add_node(NodeKind::scope(range));
236
+
Self { graph, root_idx }
237
+
}
238
+
239
+
pub fn get_node(&self, node_idx: NodeIndex) -> Option<&NodeKind> {
240
+
self.graph.node_weight(node_idx)
241
+
}
242
+
243
+
/// Insert a local scope into the scope-graph
244
+
fn insert_local_scope(&mut self, new: LocalScope) {
245
+
if let Some(parent_scope) = self.scope_by_range(new.range, self.root_idx) {
246
+
let new_scope = NodeKind::Scope(new);
247
+
let new_idx = self.graph.add_node(new_scope);
248
+
self.graph
249
+
.add_edge(new_idx, parent_scope, EdgeKind::ScopeToScope);
250
+
}
251
+
}
252
+
253
+
/// Insert a def into the scope-graph
254
+
fn insert_local_def(&mut self, new: LocalDef) {
255
+
if let Some(defining_scope) = self.scope_by_range(new.range, self.root_idx) {
256
+
let new_def = NodeKind::Def(new);
257
+
let new_idx = self.graph.add_node(new_def);
258
+
self.graph
259
+
.add_edge(new_idx, defining_scope, EdgeKind::DefToScope);
260
+
}
261
+
}
262
+
263
+
/// Insert a def into the scope-graph, at the root scope
264
+
#[allow(unused)]
265
+
fn insert_global_def(&mut self, new: LocalDef) {
266
+
let new_def = NodeKind::Def(new);
267
+
let new_idx = self.graph.add_node(new_def);
268
+
self.graph
269
+
.add_edge(new_idx, self.root_idx, EdgeKind::DefToScope);
270
+
}
271
+
272
+
/// Insert an import into the scope-graph
273
+
fn insert_local_import(&mut self, new: LocalImport) {
274
+
if let Some(defining_scope) = self.scope_by_range(new.range, self.root_idx) {
275
+
let new_imp = NodeKind::Import(new);
276
+
let new_idx = self.graph.add_node(new_imp);
277
+
self.graph
278
+
.add_edge(new_idx, defining_scope, EdgeKind::ImportToScope);
279
+
}
280
+
}
281
+
282
+
/// Insert a ref into the scope-graph
283
+
fn insert_ref(&mut self, new: Reference) {
284
+
let mut possible_defs = vec![];
285
+
let mut possible_imports = vec![];
286
+
if let Some(local_scope_idx) = self.scope_by_range(new.range, self.root_idx) {
287
+
// traverse the scopes from the current-scope to the root-scope
288
+
for scope in self.scope_stack(local_scope_idx) {
289
+
// find candidate definitions in each scope
290
+
for local_def in self
291
+
.graph
292
+
.edges_directed(scope, Direction::Incoming)
293
+
.filter(|edge| *edge.weight() == EdgeKind::DefToScope)
294
+
.map(|edge| edge.source())
295
+
{
296
+
if let NodeKind::Def(def) = &self.graph[local_def] {
297
+
if new.text == def.text {
298
+
match (def.symbol.as_ref(), new.symbol.as_ref()) {
299
+
// both contain symbols, but they don't belong to the same namepspace
300
+
(Some(d), Some(r)) if d != r => {}
301
+
302
+
// in all other cases, form an edge from the ref to def.
303
+
// an empty symbol belongs to all namespaces:
304
+
// * (None, None)
305
+
// * (None, Some(_))
306
+
// * (Some(_), None)
307
+
// * (Some(_), Some(_)) if def.namespace == ref.namespace
308
+
_ => {
309
+
possible_defs.push(local_def);
310
+
}
311
+
};
312
+
}
313
+
}
314
+
}
315
+
316
+
// find candidate imports in each scope
317
+
for local_import in self
318
+
.graph
319
+
.edges_directed(scope, Direction::Incoming)
320
+
.filter(|edge| *edge.weight() == EdgeKind::ImportToScope)
321
+
.map(|edge| edge.source())
322
+
{
323
+
if let NodeKind::Import(import) = &self.graph[local_import] {
324
+
if new.text == import.text {
325
+
possible_imports.push(local_import);
326
+
}
327
+
}
328
+
}
329
+
}
330
+
}
331
+
332
+
if !possible_defs.is_empty() || !possible_imports.is_empty() {
333
+
let new_ref = NodeKind::Ref(new);
334
+
let ref_idx = self.graph.add_node(new_ref);
335
+
for def_idx in possible_defs {
336
+
self.graph.add_edge(ref_idx, def_idx, EdgeKind::RefToDef);
337
+
}
338
+
for imp_idx in possible_imports {
339
+
self.graph.add_edge(ref_idx, imp_idx, EdgeKind::RefToImport);
340
+
}
341
+
}
342
+
}
343
+
344
+
fn scope_stack(&self, start: NodeIndex) -> ScopeStack<'_> {
345
+
ScopeStack {
346
+
scope_graph: self,
347
+
start: Some(start),
348
+
}
349
+
}
350
+
351
+
// The smallest scope that encompasses `range`. Start at `start` and narrow down if possible.
352
+
fn scope_by_range(&self, range: TextRange, start: NodeIndex) -> Option<NodeIndex> {
353
+
let target_range = self.graph[start].range();
354
+
if target_range.contains(&range) {
355
+
let mut child_scopes = self
356
+
.graph
357
+
.edges_directed(start, Direction::Incoming)
358
+
.filter(|edge| *edge.weight() == EdgeKind::ScopeToScope)
359
+
.map(|edge| edge.source())
360
+
.collect::<Vec<_>>();
361
+
362
+
child_scopes.sort_by_key(|s| self.graph[*s].range());
363
+
let target_child_scope = child_scopes.binary_search_by(|x| {
364
+
if self.graph[*x].range().contains(&range) {
365
+
std::cmp::Ordering::Equal
366
+
} else {
367
+
self.graph[*x].range().cmp(&range)
368
+
}
369
+
});
370
+
371
+
if let Some(t) = target_child_scope
372
+
.ok()
373
+
.and_then(|idx| child_scopes.get(idx))
374
+
.and_then(|s| self.scope_by_range(range, *s))
375
+
{
376
+
return Some(t);
377
+
} else {
378
+
return Some(start);
379
+
}
380
+
}
381
+
None
382
+
}
383
+
384
+
/// Produce a list of interesting ranges: ranges of defs and refs
385
+
pub fn hoverable_ranges(&self) -> Box<dyn Iterator<Item = TextRange> + '_> {
386
+
let iterator =
387
+
self.graph
388
+
.node_indices()
389
+
.filter_map(|node_idx| match &self.graph[node_idx] {
390
+
NodeKind::Scope(_) => None,
391
+
NodeKind::Def(d) => Some(d.range),
392
+
NodeKind::Ref(r) => Some(r.range),
393
+
NodeKind::Import(i) => Some(i.range),
394
+
});
395
+
Box::new(iterator)
396
+
}
397
+
398
+
/// Produce possible definitions for a reference
399
+
pub fn definitions(
400
+
&self,
401
+
reference_node: NodeIndex,
402
+
) -> Box<dyn Iterator<Item = NodeIndex> + '_> {
403
+
let iterator = self
404
+
.graph
405
+
.edges_directed(reference_node, Direction::Outgoing)
406
+
.filter(|edge| *edge.weight() == EdgeKind::RefToDef)
407
+
.map(|edge| edge.target());
408
+
Box::new(iterator)
409
+
}
410
+
411
+
/// Produce possible imports for a reference
412
+
pub fn imports(&self, reference_node: NodeIndex) -> Box<dyn Iterator<Item = NodeIndex> + '_> {
413
+
let iterator = self
414
+
.graph
415
+
.edges_directed(reference_node, Direction::Outgoing)
416
+
.filter(|edge| *edge.weight() == EdgeKind::RefToImport)
417
+
.map(|edge| edge.target());
418
+
Box::new(iterator)
419
+
}
420
+
421
+
/// Produce possible references for a definition/import node
422
+
pub fn references(
423
+
&self,
424
+
definition_node: NodeIndex,
425
+
) -> Box<dyn Iterator<Item = NodeIndex> + '_> {
426
+
let iterator = self
427
+
.graph
428
+
.edges_directed(definition_node, Direction::Incoming)
429
+
.filter(|edge| {
430
+
*edge.weight() == EdgeKind::RefToDef || *edge.weight() == EdgeKind::RefToImport
431
+
})
432
+
.map(|edge| edge.source());
433
+
Box::new(iterator)
434
+
}
435
+
436
+
pub fn node_by_range(&self, start_byte: usize, end_byte: usize) -> Option<NodeIndex> {
437
+
self.graph
438
+
.node_indices()
439
+
.filter(|&idx| self.is_definition(idx) || self.is_reference(idx) || self.is_import(idx))
440
+
.find(|&idx| {
441
+
let node = self.graph[idx].range();
442
+
start_byte >= node.start.byte && end_byte <= node.end.byte
443
+
})
444
+
}
445
+
446
+
pub fn node_by_position(&self, line: usize, column: usize) -> Option<NodeIndex> {
447
+
self.graph
448
+
.node_indices()
449
+
.filter(|&idx| self.is_definition(idx) || self.is_reference(idx))
450
+
.find(|&idx| {
451
+
let node = self.graph[idx].range();
452
+
node.start.line == line
453
+
&& node.end.line == line
454
+
&& node.start.column <= column
455
+
&& node.end.column >= column
456
+
})
457
+
}
458
+
459
+
#[cfg(test)]
460
+
pub fn find_node_by_name(&self, src: &[u8], name: &[u8]) -> Option<NodeIndex> {
461
+
self.graph.node_indices().find(|idx| {
462
+
matches!(
463
+
&self.graph[*idx],
464
+
NodeKind::Def(d) if d.name(src) == name)
465
+
})
466
+
}
467
+
468
+
#[cfg(test)]
469
+
pub fn debug(&self, src: &[u8]) -> debug::ScopeDebug {
470
+
let graph = &self.graph;
471
+
let start = self.root_idx;
472
+
debug::ScopeDebug::new(graph, start, src)
473
+
}
474
+
475
+
pub fn is_definition(&self, node_idx: NodeIndex) -> bool {
476
+
matches!(self.graph[node_idx], NodeKind::Def(_))
477
+
}
478
+
479
+
pub fn is_reference(&self, node_idx: NodeIndex) -> bool {
480
+
matches!(self.graph[node_idx], NodeKind::Ref(_))
481
+
}
482
+
483
+
pub fn is_scope(&self, node_idx: NodeIndex) -> bool {
484
+
matches!(self.graph[node_idx], NodeKind::Scope(_))
485
+
}
486
+
487
+
pub fn is_import(&self, node_idx: NodeIndex) -> bool {
488
+
matches!(self.graph[node_idx], NodeKind::Import(_))
489
+
}
490
+
}
491
+
492
+
fn build_scope_graph(
493
+
tsg: tree_sitter_graph::graph::Graph,
494
+
mut scope_graph: ScopeGraph,
495
+
) -> ScopeGraph {
496
+
let nodes = tsg.iter_nodes().collect::<Vec<_>>();
497
+
for node in nodes
498
+
.iter()
499
+
.map(|node_ref| &tsg[*node_ref])
500
+
.filter(|node| is_scope(node))
501
+
{
502
+
let range =
503
+
text_range::value_to_range(node.attributes.get(&Identifier::from("range")).unwrap());
504
+
let scope = LocalScope::new(range.into());
505
+
scope_graph.insert_local_scope(scope);
506
+
}
507
+
508
+
for node in nodes
509
+
.iter()
510
+
.map(|node_ref| &tsg[*node_ref])
511
+
.filter(|node| is_import(node))
512
+
{
513
+
let range =
514
+
text_range::value_to_range(node.attributes.get(&Identifier::from("range")).unwrap());
515
+
let text = node
516
+
.attributes
517
+
.get(&Identifier::from("text"))
518
+
.and_then(|id| id.clone().into_string().ok())
519
+
.expect("import without text");
520
+
let import = LocalImport::new(range.into(), text);
521
+
scope_graph.insert_local_import(import);
522
+
}
523
+
524
+
for node in nodes
525
+
.iter()
526
+
.map(|node_ref| &tsg[*node_ref])
527
+
.filter(|node| is_def(node))
528
+
{
529
+
let range =
530
+
text_range::value_to_range(node.attributes.get(&Identifier::from("range")).unwrap());
531
+
let symbol = node
532
+
.attributes
533
+
.get(&Identifier::from("symbol"))
534
+
.and_then(|id| id.clone().into_string().ok());
535
+
let text = node
536
+
.attributes
537
+
.get(&Identifier::from("text"))
538
+
.and_then(|id| id.clone().into_string().ok())
539
+
.expect("def without text");
540
+
let local_def = LocalDef::new(range.into(), text, symbol);
541
+
542
+
// TODO: fix scoping here
543
+
scope_graph.insert_local_def(local_def);
544
+
}
545
+
546
+
for node in nodes
547
+
.iter()
548
+
.map(|node_ref| &tsg[*node_ref])
549
+
.filter(|node| is_ref(node))
550
+
{
551
+
let range =
552
+
text_range::value_to_range(node.attributes.get(&Identifier::from("range")).unwrap());
553
+
let symbol = node
554
+
.attributes
555
+
.get(&Identifier::from("symbol"))
556
+
.and_then(|id| id.clone().into_string().ok());
557
+
let text = node
558
+
.attributes
559
+
.get(&Identifier::from("text"))
560
+
.and_then(|id| id.clone().into_string().ok())
561
+
.expect("ref without text");
562
+
let ref_ = Reference::new(range.into(), text, symbol);
563
+
564
+
scope_graph.insert_ref(ref_);
565
+
}
566
+
567
+
scope_graph
568
+
}
569
+
570
+
fn is_string_attr(node: &tree_sitter_graph::graph::GraphNode, key: &str, value: &str) -> bool {
571
+
matches!(node.attributes.get(&Identifier::from(key)).and_then(|v| v.as_str().ok()), Some(v) if v == value)
572
+
}
573
+
574
+
fn is_scope(node: &tree_sitter_graph::graph::GraphNode) -> bool {
575
+
is_string_attr(node, "kind", "scope")
576
+
}
577
+
578
+
fn is_import(node: &tree_sitter_graph::graph::GraphNode) -> bool {
579
+
is_string_attr(node, "kind", "import")
580
+
}
581
+
582
+
fn is_def(node: &tree_sitter_graph::graph::GraphNode) -> bool {
583
+
is_string_attr(node, "kind", "def")
584
+
}
585
+
586
+
fn is_ref(node: &tree_sitter_graph::graph::GraphNode) -> bool {
587
+
is_string_attr(node, "kind", "ref")
588
+
}
589
+
590
+
#[cfg(test)]
591
+
mod tests {
592
+
use super::*;
593
+
use expect_test::{expect, Expect};
594
+
595
+
fn counts(src: &str) -> (usize, usize, usize, usize) {
596
+
let sg = build_graph(src);
597
+
let nodes = sg.graph.node_weights();
598
+
599
+
nodes.fold((0, 0, 0, 0), |(s, d, r, i), node| match node {
600
+
NodeKind::Scope(_) => (s + 1, d, r, i),
601
+
NodeKind::Def(_) => (s, d + 1, r, i),
602
+
NodeKind::Ref(_) => (s, d, r + 1, i),
603
+
NodeKind::Import(_) => (s, d, r, i + 1),
604
+
})
605
+
}
606
+
607
+
pub fn test_scopes(src: &str, expected: Expect) {
608
+
let graph = build_graph(src);
609
+
let observed = graph.debug(src.as_bytes());
610
+
expected.assert_debug_eq(&observed)
611
+
}
612
+
613
+
pub fn build_graph(src: &str) -> ScopeGraph {
614
+
StagBuilder::default()
615
+
.with_source(src)
616
+
.with_stag_file(include_str!("stag.tsg"))
617
+
.with_language(tree_sitter_rust::language())
618
+
.execute()
619
+
.unwrap()
620
+
}
621
+
622
+
#[test]
623
+
fn declare_const_and_static() {
624
+
let src = r#"
625
+
const a: () = ();
626
+
static b: () = ();
627
+
"#;
628
+
629
+
let (_, def_count, _, _) = counts(src);
630
+
631
+
// a, b
632
+
assert_eq!(def_count, 2);
633
+
}
634
+
635
+
#[test]
636
+
fn declare_let_statement() {
637
+
let src = r#"
638
+
fn main() {
639
+
let a = ();
640
+
let (b, c) = ();
641
+
let S { d, e } = ();
642
+
let S { field: f, g } = ();
643
+
let S { h, .. } = ();
644
+
let S { i, field: _ } = ();
645
+
}
646
+
"#;
647
+
let (_, def_count, _, _) = counts(src);
648
+
649
+
// main, a, b, c, d, e, f, g, h, i
650
+
assert_eq!(def_count, 10);
651
+
}
652
+
653
+
#[test]
654
+
fn declare_function_params() {
655
+
let src = r#"
656
+
fn f1(a: T) {}
657
+
fn f2(b: T, c: T) {}
658
+
fn f3((d, e): (T, U)) {}
659
+
fn f4(S {f, g}: S) {}
660
+
fn f5(S {h, ..}: S) {}
661
+
fn f6(S { field: i }: S) {}
662
+
"#;
663
+
let (_, def_count, _, _) = counts(src);
664
+
665
+
// f1, f2, f3, f4, f5, f6, a, b, c, d, e, f, g, h, i
666
+
assert_eq!(def_count, 15);
667
+
}
668
+
669
+
#[test]
670
+
fn declare_closure_params() {
671
+
let src = r#"
672
+
fn main() {
673
+
let _ = |x| {};
674
+
let _ = |x, y| {};
675
+
let _ = |x: ()| {};
676
+
let _ = |(x, y): ()| {};
677
+
}
678
+
"#;
679
+
let (_, def_count, _, _) = counts(src);
680
+
681
+
// main,
682
+
// x,
683
+
// x, y,
684
+
// x,
685
+
// x, y
686
+
assert_eq!(def_count, 7);
687
+
}
688
+
689
+
#[test]
690
+
fn declare_labels() {
691
+
let src = r#"
692
+
fn main() {
693
+
'loop: loop {};
694
+
'loop: for _ in () {}
695
+
'loop: while true {}
696
+
}
697
+
"#;
698
+
let (_, def_count, _, _) = counts(src);
699
+
700
+
// main, 'loop x3
701
+
assert_eq!(def_count, 4);
702
+
}
703
+
704
+
#[test]
705
+
fn declare_types() {
706
+
let src = r#"
707
+
struct One {
708
+
two: T,
709
+
three: T,
710
+
}
711
+
712
+
enum Four {
713
+
Five,
714
+
Six(T),
715
+
Seven {
716
+
eight: T
717
+
}
718
+
}
719
+
720
+
union Nine {}
721
+
722
+
type Ten = ();
723
+
"#;
724
+
let (_, def_count, _, _) = counts(src);
725
+
726
+
assert_eq!(def_count, 10);
727
+
}
728
+
729
+
#[test]
730
+
fn declare_namespaces() {
731
+
let src = r#"
732
+
mod one {}
733
+
pub mod two {}
734
+
mod three {
735
+
mod four {}
736
+
}
737
+
"#;
738
+
let (_, def_count, _, _) = counts(src);
739
+
740
+
assert_eq!(def_count, 4);
741
+
}
742
+
743
+
#[test]
744
+
fn declare_let_expr() {
745
+
let src = r#"
746
+
if let a = () {}
747
+
if let Some(a) = () {}
748
+
749
+
while let a = () {}
750
+
while let Some(a) = () {}
751
+
"#;
752
+
let (_, def_count, _, _) = counts(src);
753
+
754
+
assert_eq!(def_count, 4);
755
+
}
756
+
757
+
#[test]
758
+
fn refer_unary_expr() {
759
+
let src = r#"
760
+
fn main() {
761
+
let a = 2;
762
+
!a;
763
+
-a;
764
+
*a;
765
+
}
766
+
"#;
767
+
let (_, _, ref_count, _) = counts(src);
768
+
769
+
assert_eq!(ref_count, 3);
770
+
}
771
+
772
+
#[test]
773
+
fn refer_binary_expr() {
774
+
let src = r#"
775
+
fn main() {
776
+
let a = 2;
777
+
let b = 3;
778
+
a + b;
779
+
a >> b;
780
+
}
781
+
"#;
782
+
let (_, _, ref_count, _) = counts(src);
783
+
784
+
assert_eq!(ref_count, 4);
785
+
}
786
+
787
+
#[test]
788
+
fn refer_control_flow() {
789
+
let src = r#"
790
+
fn main() {
791
+
let a = 2;
792
+
793
+
// 1
794
+
if a {}
795
+
796
+
// 2
797
+
if _ {} else if a {}
798
+
799
+
// 3
800
+
while a {
801
+
// 4
802
+
break a;
803
+
}
804
+
805
+
// 5
806
+
a?;
807
+
808
+
// 6
809
+
return a;
810
+
811
+
// 7
812
+
a.await;
813
+
814
+
// 8
815
+
yield a;
816
+
}
817
+
"#;
818
+
let (_, _, ref_count, _) = counts(src);
819
+
820
+
assert_eq!(ref_count, 8);
821
+
}
822
+
823
+
#[test]
824
+
fn refer_assignment() {
825
+
let src = r#"
826
+
fn main() {
827
+
let mut a = 2;
828
+
a += 2;
829
+
a = 2;
830
+
a *= 2;
831
+
}
832
+
"#;
833
+
let (_, _, ref_count, _) = counts(src);
834
+
835
+
assert_eq!(ref_count, 3);
836
+
}
837
+
838
+
#[test]
839
+
fn refer_struct_expr() {
840
+
let src = r#"
841
+
fn main() {
842
+
let a = 2;
843
+
let b = 2;
844
+
S { a, b };
845
+
S { ..a };
846
+
S { field: a, b };
847
+
}
848
+
"#;
849
+
let (_, _, ref_count, _) = counts(src);
850
+
851
+
assert_eq!(ref_count, 5);
852
+
}
853
+
854
+
#[test]
855
+
fn refer_dot() {
856
+
let src = r#"
857
+
fn main() {
858
+
let a = S {};
859
+
860
+
a.b;
861
+
a.foo();
862
+
}
863
+
"#;
864
+
let (_, _, ref_count, _) = counts(src);
865
+
866
+
assert_eq!(ref_count, 2);
867
+
}
868
+
869
+
#[test]
870
+
fn refer_arguments() {
871
+
let src = r#"
872
+
fn main() {
873
+
let a = 2;
874
+
let b = 3;
875
+
foo(a, b);
876
+
}
877
+
"#;
878
+
let (_, _, ref_count, _) = counts(src);
879
+
880
+
assert_eq!(ref_count, 2);
881
+
}
882
+
883
+
#[test]
884
+
fn function_params() {
885
+
test_scopes(
886
+
r#"
887
+
fn foo(t: T, u: U) -> R {}
888
+
"#,
889
+
expect![[r#"
890
+
scope {
891
+
definitions: [
892
+
foo {
893
+
context: "fn §foo§(t: T, u: U) -> R {}",
894
+
},
895
+
],
896
+
child scopes: [
897
+
scope {
898
+
definitions: [
899
+
t {
900
+
context: "fn foo(§t§: T, u: U) -> R {}",
901
+
},
902
+
u {
903
+
context: "fn foo(t: T, §u§: U) -> R {}",
904
+
},
905
+
],
906
+
child scopes: [],
907
+
},
908
+
scope {
909
+
definitions: [],
910
+
child scopes: [],
911
+
},
912
+
],
913
+
}
914
+
"#]],
915
+
);
916
+
}
917
+
918
+
#[test]
919
+
fn use_statements() {
920
+
test_scopes(
921
+
r#"
922
+
mod intelligence;
923
+
924
+
use bleep;
925
+
use super::test_utils;
926
+
use intelligence::language as lang;
927
+
use crate::text_range::{TextRange, Point};
928
+
"#,
929
+
expect![[r#"
930
+
scope {
931
+
definitions: [
932
+
intelligence {
933
+
context: "mod §intelligence§;",
934
+
referenced in (1): [
935
+
`use §intelligence§::language as lang;`,
936
+
],
937
+
},
938
+
bleep {
939
+
context: "use §bleep§;",
940
+
},
941
+
test_utils {
942
+
context: "use super::§test_utils§;",
943
+
},
944
+
lang {
945
+
context: "use intelligence::language as §lang§;",
946
+
},
947
+
TextRange {
948
+
context: "use crate::text_range::{§TextRange§, Point};",
949
+
},
950
+
Point {
951
+
context: "use crate::text_range::{TextRange, §Point§};",
952
+
},
953
+
],
954
+
child scopes: [],
955
+
}
956
+
"#]],
957
+
)
958
+
}
959
+
960
+
#[test]
961
+
fn lifetimes() {
962
+
test_scopes(
963
+
r#"
964
+
impl<'a, T> Trait for Struct<'a, T> {
965
+
fn foo<'b>(&'a self) -> &'b T { }
966
+
}
967
+
"#,
968
+
expect![[r#"
969
+
scope {
970
+
definitions: [],
971
+
child scopes: [
972
+
scope {
973
+
definitions: [
974
+
'a {
975
+
context: "impl<§'a§, T> Trait for Struct<'a, T> {",
976
+
referenced in (2): [
977
+
`impl<'a, T> Trait for Struct<§'a§, T> {`,
978
+
`fn foo<'b>(&§'a§ self) -> &'b T { }`,
979
+
],
980
+
},
981
+
T {
982
+
context: "impl<'a, §T§> Trait for Struct<'a, T> {",
983
+
referenced in (2): [
984
+
`impl<'a, T> Trait for Struct<'a, §T§> {`,
985
+
`fn foo<'b>(&'a self) -> &'b §T§ { }`,
986
+
],
987
+
},
988
+
],
989
+
child scopes: [
990
+
scope {
991
+
definitions: [
992
+
foo {
993
+
context: "fn §foo§<'b>(&'a self) -> &'b T { }",
994
+
},
995
+
'b {
996
+
context: "fn foo<§'b§>(&'a self) -> &'b T { }",
997
+
referenced in (1): [
998
+
`fn foo<'b>(&'a self) -> &§'b§ T { }`,
999
+
],
1000
+
},
1001
+
],
1002
+
child scopes: [
1003
+
scope {
1004
+
definitions: [
1005
+
self {
1006
+
context: "fn foo<'b>(&'a §self§) -> &'b T { }",
1007
+
},
1008
+
],
1009
+
child scopes: [],
1010
+
},
1011
+
scope {
1012
+
definitions: [],
1013
+
child scopes: [],
1014
+
},
1015
+
],
1016
+
},
1017
+
],
1018
+
},
1019
+
],
1020
+
}
1021
+
"#]],
1022
+
)
1023
+
}
1024
+
1025
+
#[test]
1026
+
fn generics_and_traits() {
1027
+
test_scopes(
1028
+
r#"
1029
+
trait Foo {}
1030
+
1031
+
fn foo<'a, 'b, T, U: Foo<T> + 'a>(t: T, u: U)
1032
+
where T: Foo + 'b,
1033
+
{ }
1034
+
"#,
1035
+
expect![[r#"
1036
+
scope {
1037
+
definitions: [
1038
+
Foo {
1039
+
context: "trait §Foo§ {}",
1040
+
referenced in (2): [
1041
+
`fn foo<'a, 'b, T, U: §Foo§<T> + 'a>(t: T, u: U)`,
1042
+
`where T: §Foo§ + 'b,`,
1043
+
],
1044
+
},
1045
+
foo {
1046
+
context: "fn §foo§<'a, 'b, T, U: Foo<T> + 'a>(t: T, u: U)",
1047
+
},
1048
+
'a {
1049
+
context: "fn foo<§'a§, 'b, T, U: Foo<T> + 'a>(t: T, u: U)",
1050
+
referenced in (1): [
1051
+
`fn foo<'a, 'b, T, U: Foo<T> + §'a§>(t: T, u: U)`,
1052
+
],
1053
+
},
1054
+
'b {
1055
+
context: "fn foo<'a, §'b§, T, U: Foo<T> + 'a>(t: T, u: U)",
1056
+
referenced in (1): [
1057
+
`where T: Foo + §'b§,`,
1058
+
],
1059
+
},
1060
+
T {
1061
+
context: "fn foo<'a, 'b, §T§, U: Foo<T> + 'a>(t: T, u: U)",
1062
+
referenced in (3): [
1063
+
`fn foo<'a, 'b, T, U: Foo<§T§> + 'a>(t: T, u: U)`,
1064
+
`fn foo<'a, 'b, T, U: Foo<T> + 'a>(t: §T§, u: U)`,
1065
+
`where §T§: Foo + 'b,`,
1066
+
],
1067
+
},
1068
+
U {
1069
+
context: "fn foo<'a, 'b, T, §U§: Foo<T> + 'a>(t: T, u: U)",
1070
+
referenced in (1): [
1071
+
`fn foo<'a, 'b, T, U: Foo<T> + 'a>(t: T, u: §U§)`,
1072
+
],
1073
+
},
1074
+
],
1075
+
child scopes: [
1076
+
scope {
1077
+
definitions: [],
1078
+
child scopes: [
1079
+
scope {
1080
+
definitions: [],
1081
+
child scopes: [
1082
+
scope {
1083
+
definitions: [],
1084
+
child scopes: [],
1085
+
},
1086
+
],
1087
+
},
1088
+
],
1089
+
},
1090
+
scope {
1091
+
definitions: [
1092
+
t {
1093
+
context: "fn foo<'a, 'b, T, U: Foo<T> + 'a>(§t§: T, u: U)",
1094
+
},
1095
+
u {
1096
+
context: "fn foo<'a, 'b, T, U: Foo<T> + 'a>(t: T, §u§: U)",
1097
+
},
1098
+
],
1099
+
child scopes: [],
1100
+
},
1101
+
scope {
1102
+
definitions: [],
1103
+
child scopes: [],
1104
+
},
1105
+
],
1106
+
}
1107
+
"#]],
1108
+
)
1109
+
}
1110
+
1111
+
#[test]
1112
+
fn type_constructors() {
1113
+
test_scopes(
1114
+
r#"
1115
+
struct Highlight {}
1116
+
1117
+
enum Direction { Incoming, Outgoing }
1118
+
1119
+
fn foo() -> Highlight {
1120
+
Highlight { }
1121
+
}
1122
+
1123
+
fn bar() -> Direction {
1124
+
Direction::Incoming
1125
+
}
1126
+
"#,
1127
+
expect![[r#"
1128
+
scope {
1129
+
definitions: [
1130
+
Highlight {
1131
+
context: "struct §Highlight§ {}",
1132
+
referenced in (2): [
1133
+
`fn foo() -> §Highlight§ {`,
1134
+
`§Highlight§ { }`,
1135
+
],
1136
+
},
1137
+
Direction {
1138
+
context: "enum §Direction§ { Incoming, Outgoing }",
1139
+
referenced in (2): [
1140
+
`fn bar() -> §Direction§ {`,
1141
+
`§Direction§::Incoming`,
1142
+
],
1143
+
},
1144
+
foo {
1145
+
context: "fn §foo§() -> Highlight {",
1146
+
},
1147
+
bar {
1148
+
context: "fn §bar§() -> Direction {",
1149
+
},
1150
+
],
1151
+
child scopes: [
1152
+
scope {
1153
+
definitions: [],
1154
+
child scopes: [
1155
+
scope {
1156
+
definitions: [],
1157
+
child scopes: [],
1158
+
},
1159
+
],
1160
+
},
1161
+
scope {
1162
+
definitions: [],
1163
+
child scopes: [
1164
+
scope {
1165
+
definitions: [
1166
+
Incoming {
1167
+
context: "enum Direction { §Incoming§, Outgoing }",
1168
+
},
1169
+
Outgoing {
1170
+
context: "enum Direction { Incoming, §Outgoing§ }",
1171
+
},
1172
+
],
1173
+
child scopes: [],
1174
+
},
1175
+
],
1176
+
},
1177
+
scope {
1178
+
definitions: [],
1179
+
child scopes: [],
1180
+
},
1181
+
scope {
1182
+
definitions: [],
1183
+
child scopes: [],
1184
+
},
1185
+
scope {
1186
+
definitions: [],
1187
+
child scopes: [],
1188
+
},
1189
+
scope {
1190
+
definitions: [],
1191
+
child scopes: [],
1192
+
},
1193
+
],
1194
+
}
1195
+
"#]],
1196
+
)
1197
+
}
1198
+
1199
+
#[test]
1200
+
fn macros() {
1201
+
test_scopes(
1202
+
r#"
1203
+
fn main() {
1204
+
let (a, b, c) = ();
1205
+
// top-level tokens
1206
+
assert_eq!(a, b + c);
1207
+
1208
+
// nested tokens
1209
+
println!("{}", if a { b } then { c });
1210
+
}
1211
+
"#,
1212
+
expect![[r#"
1213
+
scope {
1214
+
definitions: [
1215
+
main {
1216
+
context: "fn §main§() {",
1217
+
},
1218
+
],
1219
+
child scopes: [
1220
+
scope {
1221
+
definitions: [],
1222
+
child scopes: [],
1223
+
},
1224
+
scope {
1225
+
definitions: [
1226
+
a {
1227
+
context: "let (§a§, b, c) = ();",
1228
+
referenced in (2): [
1229
+
`assert_eq!(§a§, b + c);`,
1230
+
`println!("{}", if §a§ { b } then { c });`,
1231
+
],
1232
+
},
1233
+
b {
1234
+
context: "let (a, §b§, c) = ();",
1235
+
referenced in (2): [
1236
+
`assert_eq!(a, §b§ + c);`,
1237
+
`println!("{}", if a { §b§ } then { c });`,
1238
+
],
1239
+
},
1240
+
c {
1241
+
context: "let (a, b, §c§) = ();",
1242
+
referenced in (2): [
1243
+
`assert_eq!(a, b + §c§);`,
1244
+
`println!("{}", if a { b } then { §c§ });`,
1245
+
],
1246
+
},
1247
+
],
1248
+
child scopes: [],
1249
+
},
1250
+
],
1251
+
}
1252
+
"#]],
1253
+
)
1254
+
}
1255
+
1256
+
// Self::method and self.method can be raised as references
1257
+
#[test]
1258
+
fn handle_self_type_and_var() {
1259
+
test_scopes(
1260
+
r#"
1261
+
struct MyStruct {}
1262
+
1263
+
impl MyStruct {
1264
+
fn foo() {
1265
+
Self::foo()
1266
+
}
1267
+
1268
+
fn bar(&self) {
1269
+
self.bar()
1270
+
}
1271
+
}
1272
+
"#,
1273
+
expect![[r#"
1274
+
scope {
1275
+
definitions: [
1276
+
MyStruct {
1277
+
context: "struct §MyStruct§ {}",
1278
+
referenced in (1): [
1279
+
`impl §MyStruct§ {`,
1280
+
],
1281
+
},
1282
+
],
1283
+
child scopes: [
1284
+
scope {
1285
+
definitions: [],
1286
+
child scopes: [
1287
+
scope {
1288
+
definitions: [],
1289
+
child scopes: [],
1290
+
},
1291
+
],
1292
+
},
1293
+
scope {
1294
+
definitions: [],
1295
+
child scopes: [
1296
+
scope {
1297
+
definitions: [
1298
+
foo {
1299
+
context: "fn §foo§() {",
1300
+
referenced in (1): [
1301
+
`Self::§foo§()`,
1302
+
],
1303
+
},
1304
+
bar {
1305
+
context: "fn §bar§(&self) {",
1306
+
referenced in (1): [
1307
+
`self.§bar§()`,
1308
+
],
1309
+
},
1310
+
],
1311
+
child scopes: [
1312
+
scope {
1313
+
definitions: [],
1314
+
child scopes: [],
1315
+
},
1316
+
scope {
1317
+
definitions: [],
1318
+
child scopes: [],
1319
+
},
1320
+
scope {
1321
+
definitions: [
1322
+
self {
1323
+
context: "fn bar(&§self§) {",
1324
+
},
1325
+
],
1326
+
child scopes: [],
1327
+
},
1328
+
scope {
1329
+
definitions: [],
1330
+
child scopes: [],
1331
+
},
1332
+
],
1333
+
},
1334
+
],
1335
+
},
1336
+
],
1337
+
}
1338
+
"#]],
1339
+
)
1340
+
}
1341
+
1342
+
#[test]
1343
+
fn let_else_1_65_support() {
1344
+
test_scopes(
1345
+
r#"
1346
+
fn main() {
1347
+
let a = 3;
1348
+
if let b = a
1349
+
&& let c = b
1350
+
&& let d = c {
1351
+
d
1352
+
} else {
1353
+
return;
1354
+
}
1355
+
}
1356
+
"#,
1357
+
expect![[r#"
1358
+
scope {
1359
+
definitions: [
1360
+
main {
1361
+
context: "fn §main§() {",
1362
+
},
1363
+
],
1364
+
child scopes: [
1365
+
scope {
1366
+
definitions: [],
1367
+
child scopes: [],
1368
+
},
1369
+
scope {
1370
+
definitions: [
1371
+
a {
1372
+
context: "let §a§ = 3;",
1373
+
referenced in (1): [
1374
+
`if let b = §a§`,
1375
+
],
1376
+
},
1377
+
],
1378
+
child scopes: [
1379
+
scope {
1380
+
definitions: [
1381
+
b {
1382
+
context: "if let §b§ = a",
1383
+
referenced in (1): [
1384
+
`&& let c = §b§`,
1385
+
],
1386
+
},
1387
+
c {
1388
+
context: "&& let §c§ = b",
1389
+
referenced in (1): [
1390
+
`&& let d = §c§ {`,
1391
+
],
1392
+
},
1393
+
d {
1394
+
context: "&& let §d§ = c {",
1395
+
},
1396
+
],
1397
+
child scopes: [],
1398
+
},
1399
+
scope {
1400
+
definitions: [],
1401
+
child scopes: [],
1402
+
},
1403
+
scope {
1404
+
definitions: [],
1405
+
child scopes: [],
1406
+
},
1407
+
],
1408
+
},
1409
+
],
1410
+
}
1411
+
"#]],
1412
+
)
1413
+
}
1414
+
}
+30
src/main.rs
+30
src/main.rs
···
···
1
+
use stag::StagBuilder;
2
+
3
+
fn main() {
4
+
let scopes = std::fs::read_to_string("src/stag.tsg").unwrap();
5
+
let src = r#"
6
+
fn main() {
7
+
let x = 2;
8
+
let a = 5;
9
+
if let _ = z {
10
+
a[x];
11
+
}
12
+
}
13
+
"#;
14
+
15
+
let sg = StagBuilder::default()
16
+
.with_source(src)
17
+
.with_stag_file(&scopes)
18
+
.with_language(tree_sitter_rust::language())
19
+
.execute()
20
+
.unwrap();
21
+
22
+
for edge in sg.graph.raw_edges() {
23
+
let s = edge.source();
24
+
let t = edge.target();
25
+
let sn = &sg.graph[s];
26
+
let st = &sg.graph[t];
27
+
println!("{:?} -> {:?}", edge.source(), edge.target());
28
+
println!("{:#?} -> {:#?}", sn, st);
29
+
}
30
+
}
+463
src/scopes.scm
+463
src/scopes.scm
···
···
1
+
;; see tree-sitter-rust/src/grammar.json for an exhaustive list of productions
2
+
3
+
;; scopes
4
+
(block) @local.scope ; { ... }
5
+
(function_item) @local.scope
6
+
(declaration_list) @local.scope ; mod { ... }
7
+
8
+
;; impl items can define types and lifetimes:
9
+
;;
10
+
;; impl<'a, T> Trait for Struct { .. }
11
+
;;
12
+
;; in order to constrain those to the impl block,
13
+
;; we add a local scope here:
14
+
(impl_item) @local.scope
15
+
(struct_item) @local.scope
16
+
(enum_item) @local.scope
17
+
(union_item) @local.scope
18
+
(type_item) @local.scope
19
+
(trait_item) @local.scope
20
+
21
+
;; let expressions create scopes
22
+
(if_expression
23
+
[(let_condition)
24
+
(let_chain)]) @local.scope
25
+
26
+
;; each match arm can bind variables with
27
+
;; patterns, without creating a block scope;
28
+
;;
29
+
;; match _ {
30
+
;; (a, b) => a,
31
+
;; }
32
+
;;
33
+
;; The bindings for a, b are constrained to
34
+
;; the match arm.
35
+
(match_arm) @local.scope
36
+
37
+
;; loop labels are defs that are available only
38
+
;; within the scope they create:
39
+
;;
40
+
;; 'outer: loop {
41
+
;; let x = 2;
42
+
;; };
43
+
;; let y = 2;
44
+
;;
45
+
;; Produces a scope graph like so:
46
+
;;
47
+
;; {
48
+
;; defs: [ y ],
49
+
;; scopes: [
50
+
;; {
51
+
;; defs: [ 'outer ],
52
+
;; scopes: [
53
+
;; {
54
+
;; defs: [ x ]
55
+
;; }
56
+
;; ]
57
+
;; }
58
+
;; ]
59
+
;; }
60
+
;;
61
+
(loop_expression) @local.scope
62
+
(for_expression) @local.scope
63
+
(while_expression) @local.scope
64
+
65
+
66
+
;; defs
67
+
68
+
;; let x = ...;
69
+
(let_declaration
70
+
pattern: (identifier) @local.definition.variable)
71
+
72
+
;; if let x = ...;
73
+
;; while let x = ...;
74
+
(let_condition
75
+
.
76
+
(identifier) @local.definition.variable)
77
+
78
+
;; let (a, b, ...) = ..;
79
+
;; if let (a, b, ...) = {}
80
+
;; while let (a, b, ...) = {}
81
+
;; match _ { (a, b) => { .. } }
82
+
(tuple_pattern (identifier) @local.definition.variable)
83
+
84
+
;; Some(a)
85
+
(tuple_struct_pattern
86
+
type: (_)
87
+
(identifier) @local.definition.variable)
88
+
89
+
;; let S { field: a } = ..;
90
+
(struct_pattern
91
+
(field_pattern
92
+
(identifier) @local.definition.variable))
93
+
94
+
;; let S { a, b } = ..;
95
+
(struct_pattern
96
+
(field_pattern
97
+
(shorthand_field_identifier) @local.definition.variable))
98
+
99
+
;; (mut x: T)
100
+
(mut_pattern (identifier) @local.definition.variable)
101
+
102
+
;; (ref x: T)
103
+
(ref_pattern (identifier) @local.definition.variable)
104
+
105
+
;; const x = ...;
106
+
(const_item (identifier) @local.definition.const)
107
+
108
+
;; static x = ...;
109
+
(static_item (identifier) @local.definition.const)
110
+
111
+
;; fn _(x: _)
112
+
(parameters
113
+
(parameter
114
+
pattern: (identifier) @local.definition.variable))
115
+
;; fn _(self)
116
+
(parameters
117
+
(self_parameter
118
+
(self) @local.definition.variable))
119
+
120
+
;; type parameters
121
+
(type_parameters
122
+
(type_identifier) @local.definition.typedef)
123
+
(type_parameters
124
+
(lifetime) @local.definition.lifetime)
125
+
(constrained_type_parameter
126
+
left: (type_identifier) @local.definition.typedef)
127
+
128
+
;; |x| { ... }
129
+
;; no type
130
+
(closure_parameters (identifier) @local.definition.variable)
131
+
132
+
;; |x: T| { ... }
133
+
;; with type
134
+
(closure_parameters
135
+
(parameter
136
+
(identifier) @local.definition.variable))
137
+
138
+
;;fn x(..)
139
+
(function_item (identifier) @hoist.definition.function)
140
+
141
+
;; 'outer: loop { .. }
142
+
(loop_expression
143
+
(loop_label) @local.definition.label)
144
+
145
+
;; `for` exprs create two defs: a label (if any) and the
146
+
;; loop variable
147
+
(for_expression . (identifier) @local.definition.variable)
148
+
(for_expression (loop_label) @local.definition.label)
149
+
150
+
;; 'label: while cond { .. }
151
+
(while_expression
152
+
(loop_label) @local.definition.label)
153
+
154
+
;; type definitions
155
+
(struct_item (type_identifier) @hoist.definition.struct)
156
+
(enum_item (type_identifier) @hoist.definition.enum)
157
+
(union_item (type_identifier) @hoist.definition.union)
158
+
(type_item . (type_identifier) @hoist.definition.typedef)
159
+
(trait_item (type_identifier) @hoist.definition.interface)
160
+
161
+
;; struct and union fields
162
+
(field_declaration_list
163
+
(field_declaration
164
+
(field_identifier) @local.definition.field))
165
+
166
+
;; enum variants
167
+
(enum_variant_list
168
+
(enum_variant
169
+
(identifier) @local.definition.enumerator))
170
+
171
+
;; mod x;
172
+
(mod_item (identifier) @local.definition.module)
173
+
174
+
;; use statements
175
+
176
+
;; use item;
177
+
(use_declaration
178
+
(identifier) @local.import)
179
+
180
+
;; use path as item;
181
+
(use_as_clause
182
+
alias: (identifier) @local.import)
183
+
184
+
;; use path::item;
185
+
(use_declaration
186
+
(scoped_identifier
187
+
name: (identifier) @local.import))
188
+
189
+
;; use module::{member1, member2, member3};
190
+
(use_list
191
+
(identifier) @local.import)
192
+
(use_list
193
+
(scoped_identifier
194
+
name: (identifier) @local.import))
195
+
196
+
197
+
;; refs
198
+
199
+
;; !x
200
+
(unary_expression (identifier) @local.reference)
201
+
202
+
;; &x
203
+
(reference_expression (identifier) @local.reference)
204
+
205
+
;; (x)
206
+
(parenthesized_expression (identifier) @local.reference)
207
+
208
+
;; x?
209
+
(try_expression (identifier) @local.reference)
210
+
211
+
;; a = b
212
+
(assignment_expression (identifier) @local.reference)
213
+
214
+
;; a op b
215
+
(binary_expression (identifier) @local.reference)
216
+
217
+
;; a op= b
218
+
(compound_assignment_expr (identifier) @local.reference)
219
+
220
+
;; a as b
221
+
(type_cast_expression (identifier) @local.reference)
222
+
223
+
;; a()
224
+
(call_expression (identifier) @local.reference)
225
+
226
+
;; Self::foo()
227
+
;;
228
+
;; `foo` can be resolved
229
+
(call_expression
230
+
(scoped_identifier
231
+
(identifier) @_self_type
232
+
(identifier) @local.reference)
233
+
(#match? @_self_type "Self"))
234
+
235
+
;; self.foo()
236
+
;;
237
+
;; `foo` can be resolved
238
+
(call_expression
239
+
(field_expression
240
+
(self)
241
+
(field_identifier) @local.reference))
242
+
243
+
;; return a
244
+
(return_expression (identifier) @local.reference)
245
+
246
+
;; break a
247
+
(break_expression (identifier) @local.reference)
248
+
249
+
;; break 'label
250
+
(break_expression (loop_label) @local.reference)
251
+
252
+
;; continue 'label;
253
+
(continue_expression (loop_label) @local.reference)
254
+
255
+
;; yield x;
256
+
(yield_expression (identifier) @local.reference)
257
+
258
+
;; await a
259
+
(await_expression (identifier) @local.reference)
260
+
261
+
;; (a, b)
262
+
(tuple_expression (identifier) @local.reference)
263
+
264
+
;; a[]
265
+
(index_expression (identifier) @local.reference)
266
+
267
+
;; ident;
268
+
(expression_statement (identifier) @local.reference)
269
+
270
+
;; a..b
271
+
(range_expression (identifier) @local.reference)
272
+
273
+
;; [ident; N]
274
+
(array_expression (identifier) @local.reference)
275
+
276
+
;; path::to::item
277
+
;;
278
+
;; `path` is a ref
279
+
(scoped_identifier
280
+
path: (identifier) @local.reference)
281
+
282
+
;; rhs of let decls
283
+
(let_declaration
284
+
value: (identifier) @local.reference)
285
+
286
+
;; type T = [T; N]
287
+
;;
288
+
;; N is a ident ref
289
+
(array_type
290
+
length: (identifier) @local.reference)
291
+
292
+
;; S { _ }
293
+
(struct_expression
294
+
(type_identifier) @local.reference)
295
+
296
+
;; S { a }
297
+
(struct_expression
298
+
(field_initializer_list
299
+
(shorthand_field_initializer
300
+
(identifier) @local.reference)))
301
+
302
+
;; S { a: value }
303
+
(struct_expression
304
+
(field_initializer_list
305
+
(field_initializer
306
+
(identifier) @local.reference)))
307
+
308
+
;; S { ..a }
309
+
(struct_expression
310
+
(field_initializer_list
311
+
(base_field_initializer
312
+
(identifier) @local.reference)))
313
+
314
+
;; if a {}
315
+
(if_expression (identifier) @local.reference)
316
+
317
+
;; for pattern in value {}
318
+
;;
319
+
;; `value` is a ref
320
+
(for_expression
321
+
value: (identifier) @local.reference)
322
+
323
+
;; while a {}
324
+
(while_expression (identifier) @local.reference)
325
+
326
+
;; if let _ = a {}
327
+
;;
328
+
;; the ident following the `=` is a ref
329
+
;; the ident preceding the `=` is a def
330
+
;; while let _ = a {}
331
+
(let_condition
332
+
"="
333
+
(identifier) @local.reference)
334
+
335
+
336
+
;; match a
337
+
(match_expression (identifier) @local.reference)
338
+
339
+
;; match _ {
340
+
;; pattern => a,
341
+
;; }
342
+
;;
343
+
;; this `a` is somehow not any expression form
344
+
(match_arm (identifier) @local.reference)
345
+
346
+
;; a.b
347
+
;;
348
+
;; `b` is ignored
349
+
(field_expression
350
+
(identifier) @local.reference)
351
+
352
+
;; { stmt; foo }
353
+
(block
354
+
(identifier) @local.reference)
355
+
356
+
;; arguments to method calls or function calls
357
+
(arguments
358
+
(identifier) @local.reference)
359
+
360
+
;; impl S { .. }
361
+
(impl_item (type_identifier) @local.reference)
362
+
363
+
;; where T: ...
364
+
(where_predicate
365
+
left: (type_identifier) @local.reference)
366
+
367
+
;; trait bounds
368
+
(trait_bounds
369
+
(type_identifier) @local.reference)
370
+
(trait_bounds
371
+
(lifetime) @local.reference)
372
+
373
+
;; idents in macros
374
+
(token_tree
375
+
(identifier) @local.reference)
376
+
377
+
;; types
378
+
379
+
;; (T, U)
380
+
(tuple_type
381
+
(type_identifier) @local.reference)
382
+
383
+
;; &T
384
+
(reference_type
385
+
(type_identifier) @local.reference)
386
+
387
+
;; &'a T
388
+
(reference_type
389
+
(lifetime) @local.reference)
390
+
391
+
;; &'a self
392
+
(self_parameter
393
+
(lifetime) @local.reference)
394
+
395
+
;; *mut T
396
+
;; *const T
397
+
(pointer_type
398
+
(type_identifier) @local.reference)
399
+
400
+
;; A<_>
401
+
(generic_type
402
+
(type_identifier) @local.reference)
403
+
404
+
;; _<V>
405
+
(type_arguments
406
+
(type_identifier) @local.reference)
407
+
(type_arguments
408
+
(lifetime) @local.reference)
409
+
410
+
;; T<U = V>
411
+
;;
412
+
;; U is ignored
413
+
;; V is a ref
414
+
(type_binding
415
+
name: (_)
416
+
type: (type_identifier) @local.reference)
417
+
418
+
;; [T]
419
+
(array_type
420
+
(type_identifier) @local.reference)
421
+
422
+
;; type T = U;
423
+
;;
424
+
;; T is a def
425
+
;; U is a ref
426
+
(type_item
427
+
name: (_)
428
+
type: (type_identifier) @local.reference)
429
+
430
+
(function_item
431
+
return_type: (type_identifier) @local.reference)
432
+
433
+
;; type refs in params
434
+
;;
435
+
;; fn _(_: T)
436
+
(parameters
437
+
(parameter
438
+
type: (type_identifier) @local.reference))
439
+
440
+
;; dyn T
441
+
(dynamic_type
442
+
(type_identifier) @local.reference)
443
+
444
+
;; <T>::call()
445
+
(bracketed_type
446
+
(type_identifier) @local.reference)
447
+
448
+
;; T as Trait
449
+
(qualified_type
450
+
(type_identifier) @local.reference)
451
+
452
+
;; module::T
453
+
;;
454
+
;; `module` is a def
455
+
;; `T` is a ref
456
+
(scoped_type_identifier
457
+
path: (identifier) @local.reference)
458
+
459
+
;; struct _ { field: Type }
460
+
;; `Type` is a ref
461
+
(field_declaration
462
+
name: (_)
463
+
type: (type_identifier) @local.reference)
+550
src/stag.tsg
+550
src/stag.tsg
···
···
1
+
[(block)
2
+
(declaration_list)
3
+
(impl_item)
4
+
5
+
;; let expressions create scopes
6
+
(if_expression
7
+
[(let_condition)
8
+
(let_chain)])
9
+
10
+
;; each match arm can bind variables with
11
+
;; patterns, without creating a block scope;
12
+
;;
13
+
;; match _ {
14
+
;; (a, b) => a,
15
+
;; }
16
+
;;
17
+
;; The bindings for a, b are constrained to
18
+
;; the match arm.
19
+
(match_arm)
20
+
21
+
;; loop labels are defs that are available only
22
+
;; within the scope they create:
23
+
;;
24
+
;; 'outer: loop {
25
+
;; let x = 2;
26
+
;; };
27
+
;; let y = 2;
28
+
;;
29
+
;; Produces a scope graph like so:
30
+
;;
31
+
;; {
32
+
;; defs: [ y ],
33
+
;; scopes: [
34
+
;; {
35
+
;; defs: [ 'outer ],
36
+
;; scopes: [
37
+
;; {
38
+
;; defs: [ x ]
39
+
;; }
40
+
;; ]
41
+
;; }
42
+
;; ]
43
+
;; }
44
+
;;
45
+
(loop_expression)
46
+
(for_expression)
47
+
(while_expression)] @cap
48
+
{
49
+
(scope (range @cap))
50
+
}
51
+
52
+
53
+
(function_item
54
+
(identifier) @i
55
+
(parameters) @params
56
+
(block) @body)
57
+
{
58
+
(def @i "function")
59
+
(scope (cover @params @body))
60
+
}
61
+
62
+
63
+
;; impl items can define types and lifetimes:
64
+
;;
65
+
;; impl<'a, T> Trait for Struct { .. }
66
+
;;
67
+
;; in order to constrain those to the impl block,
68
+
;; we add a local scope here:
69
+
[(struct_item (type_identifier) @i (type_parameters)? @t body: (_) @b)
70
+
(union_item (type_identifier) @i (type_parameters)? @t body: (_) @b)
71
+
(enum_item (type_identifier) @i (type_parameters)? @t body: (_) @b)
72
+
(type_item (type_identifier) @i (type_parameters)? @t type: (_) @b)
73
+
(trait_item (type_identifier) @i (type_parameters)? @t body: (_) @b)]
74
+
{
75
+
(def @i)
76
+
(scope (cover @t @b))
77
+
(scope (range @b))
78
+
}
79
+
80
+
81
+
;; DEFS
82
+
;; ----
83
+
84
+
;; let x = ...;
85
+
(let_declaration pattern: (identifier) @cap)
86
+
{
87
+
(def @cap "variable")
88
+
}
89
+
90
+
;; if let x = ...;
91
+
;; while let x = ...;
92
+
(let_condition (identifier) @cap . "=")
93
+
{
94
+
(def @cap "variable")
95
+
}
96
+
97
+
;; let (a, b, ...) = ..;
98
+
;; if let (a, b, ...) = {}
99
+
;; while let (a, b, ...) = {}
100
+
;; match _ { (a, b) => { .. } }
101
+
(tuple_pattern (identifier) @cap)
102
+
{
103
+
(def @cap "variable")
104
+
}
105
+
106
+
;; Some(a)
107
+
(tuple_struct_pattern
108
+
type: (_)
109
+
(identifier) @cap)
110
+
{
111
+
(def @cap "variable")
112
+
}
113
+
114
+
;; let S { field: a } = ..;
115
+
(struct_pattern
116
+
(field_pattern
117
+
(identifier) @cap))
118
+
{
119
+
(def @cap "variable")
120
+
}
121
+
122
+
123
+
[
124
+
;; (mut x: T)
125
+
(mut_pattern (identifier) @i)
126
+
127
+
;; (ref x: T)
128
+
(ref_pattern (identifier) @i)
129
+
130
+
;; const x = ...;
131
+
(const_item (identifier) @i)
132
+
133
+
;; static x = ...;
134
+
(static_item (identifier) @i)]
135
+
{
136
+
(def @i "variable")
137
+
}
138
+
139
+
;; fn _(x: _)
140
+
(parameters
141
+
(parameter
142
+
pattern: (identifier) @cap))
143
+
{
144
+
(def @cap)
145
+
}
146
+
147
+
;; fn _(self)
148
+
(parameters
149
+
(self_parameter
150
+
(self) @cap))
151
+
{
152
+
(def @cap)
153
+
}
154
+
155
+
;; type parameters
156
+
(type_parameters
157
+
(type_identifier) @cap)
158
+
{
159
+
(def @cap)
160
+
}
161
+
(type_parameters
162
+
(lifetime) @cap)
163
+
{
164
+
(def @cap)
165
+
}
166
+
(constrained_type_parameter
167
+
left: (type_identifier) @cap)
168
+
{
169
+
(def @cap)
170
+
}
171
+
172
+
;; |x| { ... }
173
+
;; no type
174
+
(closure_parameters (identifier) @cap)
175
+
{
176
+
(def @cap)
177
+
}
178
+
179
+
;; |x: T| { ... }
180
+
;; with type
181
+
(closure_parameters
182
+
(parameter
183
+
(identifier) @cap))
184
+
{
185
+
(def @cap)
186
+
}
187
+
188
+
;; 'outer: loop { .. }
189
+
(loop_expression
190
+
(loop_label) @cap)
191
+
{
192
+
(def @cap)
193
+
}
194
+
195
+
;; `for` exprs create two defs: a label (if any) and the
196
+
;; loop variable
197
+
(for_expression . (identifier) @cap)
198
+
{
199
+
(def @cap)
200
+
}
201
+
(for_expression (loop_label) @cap)
202
+
{
203
+
(def @cap)
204
+
}
205
+
206
+
;; 'label: while cond { .. }
207
+
(while_expression
208
+
(loop_label) @cap)
209
+
{
210
+
(def @cap)
211
+
}
212
+
213
+
;; struct and union fields
214
+
(field_declaration_list
215
+
(field_declaration
216
+
(field_identifier) @cap))
217
+
{
218
+
(def @cap)
219
+
}
220
+
221
+
;; enum variants
222
+
(enum_variant_list
223
+
(enum_variant
224
+
(identifier) @cap))
225
+
{
226
+
(def @cap)
227
+
}
228
+
229
+
;; mod x;
230
+
(mod_item (identifier) @cap)
231
+
{
232
+
(def @cap)
233
+
}
234
+
235
+
236
+
;; IMPORTS
237
+
;; -------
238
+
239
+
;; use item;
240
+
(use_declaration
241
+
(identifier) @cap)
242
+
{
243
+
(def @cap)
244
+
}
245
+
246
+
;; use path as item;
247
+
(use_as_clause
248
+
alias: (identifier) @cap)
249
+
{
250
+
(def @cap)
251
+
}
252
+
253
+
;; use path::item;
254
+
(use_declaration
255
+
(scoped_identifier
256
+
name: (identifier) @cap))
257
+
{
258
+
(def @cap)
259
+
}
260
+
261
+
;; use module::{member1, member2, member3};
262
+
(use_list
263
+
(identifier) @cap)
264
+
{
265
+
(def @cap)
266
+
}
267
+
268
+
(use_list
269
+
(scoped_identifier
270
+
name: (identifier) @cap))
271
+
{
272
+
(def @cap)
273
+
}
274
+
275
+
276
+
;; REFS
277
+
;; ----
278
+
279
+
[
280
+
;; !x
281
+
(unary_expression (identifier) @cap)
282
+
;; &x
283
+
(reference_expression (identifier) @cap)
284
+
285
+
;; (x)
286
+
(parenthesized_expression (identifier) @cap)
287
+
288
+
;; x?
289
+
(try_expression (identifier) @cap)
290
+
291
+
;; a = b
292
+
(assignment_expression (identifier) @cap)
293
+
294
+
;; a op b
295
+
(binary_expression (identifier) @cap)
296
+
297
+
;; a op= b
298
+
(compound_assignment_expr (identifier) @cap)
299
+
300
+
;; a as b
301
+
(type_cast_expression (identifier) @cap)
302
+
303
+
;; a()
304
+
(call_expression (identifier) @cap)
305
+
306
+
;; return a
307
+
(return_expression (identifier) @cap)
308
+
309
+
;; break a
310
+
(break_expression (identifier) @cap)
311
+
312
+
;; break 'label
313
+
(break_expression (loop_label) @cap)
314
+
315
+
;; continue 'label;
316
+
(continue_expression (loop_label) @cap)
317
+
318
+
;; yield x;
319
+
(yield_expression (identifier) @cap)
320
+
321
+
;; await a
322
+
(await_expression (identifier) @cap)
323
+
324
+
;; (a, b)
325
+
(tuple_expression (identifier) @cap)
326
+
327
+
;; a[]
328
+
(index_expression (identifier) @cap)
329
+
330
+
;; ident;
331
+
(expression_statement (identifier) @cap)
332
+
333
+
;; a..b
334
+
(range_expression (identifier) @cap)
335
+
336
+
;; [ident; N]
337
+
(array_expression (identifier) @cap)
338
+
339
+
;; path::to::item
340
+
;;
341
+
;; `path` is a ref
342
+
(scoped_identifier
343
+
path: (identifier) @cap)
344
+
345
+
;; rhs of let decls
346
+
(let_declaration
347
+
value: (identifier) @cap)
348
+
349
+
;; type T = [T; N]
350
+
;;
351
+
;; N is a ident ref
352
+
(array_type
353
+
length: (identifier) @cap)
354
+
355
+
;; S { _ }
356
+
(struct_expression
357
+
(type_identifier) @cap)
358
+
359
+
;; S { a }
360
+
(struct_expression
361
+
(field_initializer_list
362
+
(shorthand_field_initializer
363
+
(identifier) @cap)))
364
+
365
+
;; S { a: value }
366
+
(struct_expression
367
+
(field_initializer_list
368
+
(field_initializer
369
+
(identifier) @cap)))
370
+
371
+
;; S { ..a }
372
+
(struct_expression
373
+
(field_initializer_list
374
+
(base_field_initializer
375
+
(identifier) @cap)))
376
+
377
+
;; if a {}
378
+
(if_expression (identifier) @cap)
379
+
380
+
;; for pattern in value {}
381
+
;;
382
+
;; `value` is a ref
383
+
(for_expression
384
+
value: (identifier) @cap)
385
+
386
+
;; while a {}
387
+
(while_expression (identifier) @cap)
388
+
389
+
;; match a
390
+
(match_expression (identifier) @cap)
391
+
392
+
;; match _ {
393
+
;; pattern => a,
394
+
;; }
395
+
;;
396
+
;; this `a` is somehow not any expression form
397
+
(match_arm (identifier) @cap)
398
+
399
+
;; a.b
400
+
;;
401
+
;; `b` is ignored
402
+
(field_expression
403
+
(identifier) @cap)
404
+
405
+
;; { stmt; foo }
406
+
(block
407
+
(identifier) @cap)
408
+
409
+
;; arguments to method calls or function calls
410
+
(arguments
411
+
(identifier) @cap)
412
+
413
+
;; impl S { .. }
414
+
(impl_item (type_identifier) @cap)
415
+
416
+
;; where T: ...
417
+
(where_predicate
418
+
left: (type_identifier) @cap)
419
+
420
+
;; trait bounds
421
+
(trait_bounds
422
+
(type_identifier) @cap)
423
+
(trait_bounds
424
+
(lifetime) @cap)
425
+
426
+
;; idents in macros
427
+
(token_tree
428
+
(identifier) @cap)
429
+
430
+
431
+
;; types
432
+
433
+
;; (T, U)
434
+
(tuple_type
435
+
(type_identifier) @cap)
436
+
437
+
;; &T
438
+
(reference_type
439
+
(type_identifier) @cap)
440
+
441
+
;; &'a T
442
+
(reference_type
443
+
(lifetime) @cap)
444
+
445
+
;; &'a self
446
+
(self_parameter
447
+
(lifetime) @cap)
448
+
449
+
;; *mut T
450
+
;; *const T
451
+
(pointer_type
452
+
(type_identifier) @cap)
453
+
454
+
;; A<_>
455
+
(generic_type
456
+
(type_identifier) @cap)
457
+
458
+
;; _<V>
459
+
(type_arguments
460
+
(type_identifier) @cap)
461
+
(type_arguments
462
+
(lifetime) @cap)
463
+
464
+
;; T<U = V>
465
+
;;
466
+
;; U is ignored
467
+
;; V is a ref
468
+
(type_binding
469
+
name: (_)
470
+
type: (type_identifier) @cap)
471
+
472
+
;; [T]
473
+
(array_type
474
+
(type_identifier) @cap)
475
+
476
+
;; type T = U;
477
+
;;
478
+
;; T is a def
479
+
;; U is a ref
480
+
(type_item
481
+
name: (_)
482
+
type: (type_identifier) @cap)
483
+
484
+
(function_item
485
+
return_type: (type_identifier) @cap)
486
+
487
+
;; type refs in params
488
+
;;
489
+
;; fn _(_: T)
490
+
(parameters
491
+
(parameter
492
+
type: (type_identifier) @cap))
493
+
494
+
;; dyn T
495
+
(dynamic_type
496
+
(type_identifier) @cap)
497
+
498
+
;; <T>::call()
499
+
(bracketed_type
500
+
(type_identifier) @cap)
501
+
502
+
;; T as Trait
503
+
(qualified_type
504
+
(type_identifier) @cap)
505
+
506
+
;; module::T
507
+
;;
508
+
;; `module` is a def
509
+
;; `T` is a ref
510
+
(scoped_type_identifier
511
+
path: (identifier) @cap)
512
+
513
+
;; struct _ { field: Type }
514
+
;; `Type` is a ref
515
+
(field_declaration
516
+
name: (_)
517
+
type: (type_identifier) @cap)
518
+
519
+
;; Self::foo()
520
+
;;
521
+
;; `foo` can be resolved
522
+
(call_expression
523
+
(scoped_identifier
524
+
(identifier) @_self_type
525
+
(identifier) @cap)
526
+
(#match? @_self_type "Self"))
527
+
528
+
;; self.foo()
529
+
;;
530
+
;; `foo` can be resolved
531
+
(call_expression
532
+
(field_expression
533
+
(self)
534
+
(field_identifier) @cap))
535
+
536
+
;; if let _ = a {}
537
+
;;
538
+
;; the ident following the `=` is a ref
539
+
;; the ident preceding the `=` is a def
540
+
;; while let _ = a {}
541
+
(let_condition
542
+
"="
543
+
.
544
+
(identifier) @cap)
545
+
]
546
+
{
547
+
(ref @cap)
548
+
}
549
+
550
+
+197
src/stdlib.rs
+197
src/stdlib.rs
···
···
1
+
use tree_sitter_graph::{functions::Function, ExecutionError, Identifier};
2
+
3
+
use crate::text_range;
4
+
5
+
pub struct ScopeShorthand;
6
+
7
+
#[allow(unused_must_use)]
8
+
impl Function for ScopeShorthand {
9
+
fn call(
10
+
&self,
11
+
graph: &mut tree_sitter_graph::graph::Graph,
12
+
_source: &str,
13
+
parameters: &mut dyn tree_sitter_graph::functions::Parameters,
14
+
) -> Result<tree_sitter_graph::graph::Value, ExecutionError> {
15
+
let target_range = parameters.param()?;
16
+
if target_range.as_list().is_err() {
17
+
return Err(ExecutionError::ExpectedList(format!(
18
+
"`scope` expects list"
19
+
)));
20
+
}
21
+
parameters.finish()?;
22
+
23
+
let graph_node = graph.add_graph_node();
24
+
graph[graph_node]
25
+
.attributes
26
+
.add::<String>(Identifier::from("kind"), "scope".into());
27
+
graph[graph_node]
28
+
.attributes
29
+
.add(Identifier::from("range"), target_range);
30
+
31
+
Ok(tree_sitter_graph::graph::Value::GraphNode(graph_node))
32
+
}
33
+
}
34
+
35
+
pub struct DefShorthand;
36
+
37
+
#[allow(unused_must_use)]
38
+
impl Function for DefShorthand {
39
+
fn call(
40
+
&self,
41
+
graph: &mut tree_sitter_graph::graph::Graph,
42
+
source: &str,
43
+
parameters: &mut dyn tree_sitter_graph::functions::Parameters,
44
+
) -> Result<tree_sitter_graph::graph::Value, ExecutionError> {
45
+
let target_node = parameters.param()?.into_syntax_node_ref()?;
46
+
let ts_node = graph[target_node];
47
+
let symbol = parameters
48
+
.param()
49
+
.and_then(|p| p.as_str().map(ToOwned::to_owned))
50
+
.ok();
51
+
parameters.finish()?;
52
+
53
+
let graph_node = graph.add_graph_node();
54
+
graph[graph_node]
55
+
.attributes
56
+
.add::<String>(Identifier::from("kind"), "def".into());
57
+
graph[graph_node]
58
+
.attributes
59
+
.add::<String>(Identifier::from("scope"), "local".into());
60
+
61
+
if let Some(s) = symbol {
62
+
graph[graph_node]
63
+
.attributes
64
+
.add::<String>(Identifier::from("symbol"), s.into());
65
+
}
66
+
graph[graph_node].attributes.add::<String>(
67
+
Identifier::from("text"),
68
+
source[ts_node.byte_range()].to_string().into(),
69
+
);
70
+
graph[graph_node].attributes.add(
71
+
Identifier::from("range"),
72
+
text_range::range_to_value(&ts_node.range()),
73
+
);
74
+
graph[graph_node]
75
+
.attributes
76
+
.add::<tree_sitter_graph::graph::SyntaxNodeRef>(
77
+
Identifier::from("target"),
78
+
target_node.into(),
79
+
);
80
+
81
+
Ok(tree_sitter_graph::graph::Value::GraphNode(graph_node))
82
+
}
83
+
}
84
+
85
+
pub struct RefShortHand;
86
+
87
+
#[allow(unused_must_use)]
88
+
impl Function for RefShortHand {
89
+
fn call(
90
+
&self,
91
+
graph: &mut tree_sitter_graph::graph::Graph,
92
+
source: &str,
93
+
parameters: &mut dyn tree_sitter_graph::functions::Parameters,
94
+
) -> Result<tree_sitter_graph::graph::Value, ExecutionError> {
95
+
let target_node = parameters.param()?.into_syntax_node_ref()?;
96
+
let ts_node = graph[target_node];
97
+
parameters.finish()?;
98
+
99
+
let graph_node = graph.add_graph_node();
100
+
graph[graph_node]
101
+
.attributes
102
+
.add::<String>(Identifier::from("kind"), "ref".into());
103
+
graph[graph_node].attributes.add::<String>(
104
+
Identifier::from("text"),
105
+
source[ts_node.byte_range()].to_string().into(),
106
+
);
107
+
graph[graph_node].attributes.add(
108
+
Identifier::from("range"),
109
+
text_range::range_to_value(&ts_node.range()),
110
+
);
111
+
graph[graph_node]
112
+
.attributes
113
+
.add::<tree_sitter_graph::graph::SyntaxNodeRef>(
114
+
Identifier::from("target"),
115
+
target_node.into(),
116
+
);
117
+
118
+
Ok(tree_sitter_graph::graph::Value::GraphNode(graph_node))
119
+
}
120
+
}
121
+
122
+
pub struct CoverRanges;
123
+
124
+
impl Function for CoverRanges {
125
+
fn call(
126
+
&self,
127
+
graph: &mut tree_sitter_graph::graph::Graph,
128
+
_source: &str,
129
+
parameters: &mut dyn tree_sitter_graph::functions::Parameters,
130
+
) -> Result<tree_sitter_graph::graph::Value, ExecutionError> {
131
+
let p1 = parameters.param()?;
132
+
let p2 = parameters.param()?;
133
+
134
+
match (p1.is_null(), p2.is_null()) {
135
+
(true, true) => panic!("all nulls"),
136
+
(false, true) => {
137
+
return Ok(text_range::range_to_value(
138
+
&graph[p1.into_syntax_node_ref()?].range(),
139
+
))
140
+
}
141
+
(true, false) => {
142
+
return Ok(text_range::range_to_value(
143
+
&graph[p2.into_syntax_node_ref()?].range(),
144
+
))
145
+
}
146
+
(false, false) => {
147
+
let node_a = p1.into_syntax_node_ref()?;
148
+
let node_b = p2.into_syntax_node_ref()?;
149
+
let ts_node_a = graph[node_a];
150
+
let ts_node_b = graph[node_b];
151
+
152
+
let mut range = cover(ts_node_a.range(), ts_node_b.range());
153
+
while let Ok(param) = parameters.param() {
154
+
if !param.is_null() {
155
+
range = cover(range, graph[param.into_syntax_node_ref()?].range())
156
+
}
157
+
}
158
+
159
+
Ok(text_range::range_to_value(&range))
160
+
}
161
+
}
162
+
}
163
+
}
164
+
165
+
fn cover(a: tree_sitter::Range, b: tree_sitter::Range) -> tree_sitter::Range {
166
+
let (start_byte, start_point) = if a.start_point < b.start_point {
167
+
(a.start_byte, a.start_point)
168
+
} else {
169
+
(b.start_byte, b.start_point)
170
+
};
171
+
let (end_byte, end_point) = if a.end_point > b.end_point {
172
+
(a.end_byte, a.end_point)
173
+
} else {
174
+
(b.end_byte, b.end_point)
175
+
};
176
+
tree_sitter::Range {
177
+
start_byte,
178
+
start_point,
179
+
end_byte,
180
+
end_point,
181
+
}
182
+
}
183
+
184
+
pub struct NodeRange;
185
+
186
+
impl Function for NodeRange {
187
+
fn call(
188
+
&self,
189
+
graph: &mut tree_sitter_graph::graph::Graph,
190
+
_source: &str,
191
+
parameters: &mut dyn tree_sitter_graph::functions::Parameters,
192
+
) -> Result<tree_sitter_graph::graph::Value, ExecutionError> {
193
+
let target_node = parameters.param()?.into_syntax_node_ref()?;
194
+
let ts_node = graph[target_node];
195
+
Ok(text_range::range_to_value(&ts_node.range()))
196
+
}
197
+
}
+153
src/text_range.rs
+153
src/text_range.rs
···
···
1
+
use serde::{Deserialize, Serialize};
2
+
use tree_sitter_graph::graph::Value;
3
+
4
+
use std::cmp::{Ord, Ordering};
5
+
6
+
/// A singular position in a text document
7
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
8
+
pub struct Point {
9
+
/// The byte index
10
+
pub byte: usize,
11
+
12
+
/// 0-indexed line number
13
+
pub line: usize,
14
+
15
+
/// Position within the line
16
+
pub column: usize,
17
+
}
18
+
19
+
impl PartialOrd for Point {
20
+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
21
+
Some(self.cmp(other))
22
+
}
23
+
}
24
+
25
+
impl Ord for Point {
26
+
fn cmp(&self, other: &Self) -> Ordering {
27
+
self.byte.cmp(&other.byte)
28
+
}
29
+
}
30
+
31
+
impl Point {
32
+
pub fn new(byte: usize, line: usize, column: usize) -> Self {
33
+
Self { byte, line, column }
34
+
}
35
+
36
+
pub fn from_byte(byte: usize, line_end_indices: &[u32]) -> Self {
37
+
let line = line_end_indices
38
+
.iter()
39
+
.position(|&line_end_byte| (line_end_byte as usize) > byte)
40
+
.unwrap_or(0);
41
+
42
+
let column = line
43
+
.checked_sub(1)
44
+
.and_then(|idx| line_end_indices.get(idx))
45
+
.map(|&prev_line_end| byte.saturating_sub(prev_line_end as usize))
46
+
.unwrap_or(byte);
47
+
48
+
Self::new(byte, line, column)
49
+
}
50
+
}
51
+
52
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
53
+
pub struct TextRange {
54
+
pub start: Point,
55
+
pub end: Point,
56
+
}
57
+
58
+
impl PartialOrd for TextRange {
59
+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
60
+
Some(self.cmp(other))
61
+
}
62
+
}
63
+
64
+
impl Ord for TextRange {
65
+
fn cmp(&self, other: &Self) -> Ordering {
66
+
let compare_start_byte = self.start.byte.cmp(&other.start.byte);
67
+
let compare_size = self.size().cmp(&other.size());
68
+
69
+
compare_start_byte.then(compare_size)
70
+
}
71
+
}
72
+
73
+
impl TextRange {
74
+
pub fn new(start: Point, end: Point) -> Self {
75
+
assert!(start <= end);
76
+
Self { start, end }
77
+
}
78
+
79
+
pub fn contains(&self, other: &TextRange) -> bool {
80
+
// (self.start ... [other.start ... other.end] ... self.end)
81
+
self.start <= other.start && other.end <= self.end
82
+
}
83
+
84
+
pub fn contains_strict(&self, other: TextRange) -> bool {
85
+
// (self.start ... (other.start ... other.end) ... self.end)
86
+
self.start < other.start && other.end < self.end
87
+
}
88
+
89
+
pub fn size(&self) -> usize {
90
+
self.end.byte.saturating_sub(self.start.byte)
91
+
}
92
+
93
+
pub fn from_byte_range(range: std::ops::Range<usize>, line_end_indices: &[u32]) -> Self {
94
+
let start = Point::from_byte(range.start, line_end_indices);
95
+
let end = Point::from_byte(range.end, line_end_indices);
96
+
Self::new(start, end)
97
+
}
98
+
}
99
+
100
+
impl From<tree_sitter::Range> for TextRange {
101
+
fn from(r: tree_sitter::Range) -> Self {
102
+
Self {
103
+
start: Point {
104
+
byte: r.start_byte,
105
+
line: r.start_point.row,
106
+
column: r.start_point.column,
107
+
},
108
+
end: Point {
109
+
byte: r.end_byte,
110
+
line: r.end_point.row,
111
+
column: r.end_point.column,
112
+
},
113
+
}
114
+
}
115
+
}
116
+
117
+
impl From<TextRange> for std::ops::Range<usize> {
118
+
fn from(r: TextRange) -> std::ops::Range<usize> {
119
+
r.start.byte..r.end.byte
120
+
}
121
+
}
122
+
123
+
pub fn value_to_range(value: &Value) -> tree_sitter::Range {
124
+
let range = value.as_list().unwrap();
125
+
let start = range[0].as_list().unwrap();
126
+
let end = range[1].as_list().unwrap();
127
+
tree_sitter::Range {
128
+
start_byte: start[0].as_integer().unwrap() as usize,
129
+
start_point: tree_sitter::Point {
130
+
row: start[1].as_integer().unwrap() as usize,
131
+
column: start[2].as_integer().unwrap() as usize,
132
+
},
133
+
end_byte: end[0].as_integer().unwrap() as usize,
134
+
end_point: tree_sitter::Point {
135
+
row: end[1].as_integer().unwrap() as usize,
136
+
column: end[2].as_integer().unwrap() as usize,
137
+
},
138
+
}
139
+
}
140
+
141
+
pub fn range_to_value(value: &tree_sitter::Range) -> Value {
142
+
let start = Value::from(vec![
143
+
Value::from(value.start_byte as u32),
144
+
Value::from(value.start_point.row as u32),
145
+
Value::from(value.start_point.column as u32),
146
+
]);
147
+
let end = Value::from(vec![
148
+
Value::from(value.end_byte as u32),
149
+
Value::from(value.end_point.row as u32),
150
+
Value::from(value.end_point.column as u32),
151
+
]);
152
+
Value::from(vec![start, end])
153
+
}