[rust] Improved name resolution
This is the start of an improved semantic analysis pass, reusing the ScopeManager added earlier in the stack but with new analysis built using the new visitor trait. The logic is a rough port of https://github.com/facebook/hermes/blob/main/tools/hermes-parser/js/hermes-eslint/src/scope-manager/referencer/Referencer.js Lots of bits are still missing, i'm starting with the parts that Forget needs.
Joe Savona committed
Aug 4, 2023 at 09:45 UTC
e6e2d9437e26bbb39c07a94fa9cbd6611d83bcc4
39 files changed
+1146
-447
compiler/forget/crates/forget_diagnostics/src/diagnostic.rs
+1
-1
@@ -272,7 +272,7 @@ impl From<Diagnostic> for Diagnostics {
272
273
fn source_span_from_range(range: SourceRange) -> SourceSpan {
274
SourceSpan::new(
275
- ByteOffset::from(range.start as usize - 1).into(),
275
+ ByteOffset::from(range.start as usize).into(),
276
ByteOffset::from((u32::from(range.end) - range.start) as usize).into(),
277
)
278
}
compiler/forget/crates/forget_estree/src/generated.rs
+59
-104
@@ -1993,10 +1993,15 @@ impl Serialize for ObjectPattern {
1993
}
1994
#[derive(Deserialize, Clone, Debug)]
1995
pub struct AssignmentProperty {
1996
- pub key: PropertyKey,
1996
+ pub key: Expression,
1997
pub value: Pattern,
1998
pub kind: PropertyKind,
1999
- pub method: bool,
1999
+ #[serde(rename = "computed")]
2000
+ pub is_computed: bool,
2001
+ #[serde(rename = "shorthand")]
2002
+ pub is_shorthand: bool,
2003
+ #[serde(rename = "method")]
2004
+ pub is_method: bool,
2005
#[serde(default)]
2006
pub loc: Option<SourceLocation>,
2007
#[serde(default)]
@@ -2013,7 +2018,9 @@ impl Serialize for AssignmentProperty {
2018
state.serialize_entry("key", &self.key)?;
2019
state.serialize_entry("value", &self.value)?;
2020
state.serialize_entry("kind", &self.kind)?;
2016
- state.serialize_entry("method", &self.method)?;
2021
+ state.serialize_entry("computed", &self.is_computed)?;
2022
+ state.serialize_entry("shorthand", &self.is_shorthand)?;
2023
+ state.serialize_entry("method", &self.is_method)?;
2024
state.serialize_entry("loc", &self.loc)?;
2025
state.serialize_entry("range", &self.range)?;
2026
state.end()
@@ -5287,54 +5294,17 @@ impl<'de> serde::Deserialize<'de> for AssignmentPropertyOrRestElement {
5294
}
5295
#[derive(Serialize, Clone, Debug)]
5296
#[serde(untagged)]
5290
-pub enum PropertyKey {
5291
- Identifier(Box<Identifier>),
5292
- Literal(Box<Literal>),
5293
-}
5294
-#[derive(Deserialize, Debug)]
5295
-enum __PropertyKeyTag {
5296
- Identifier,
5297
- Literal,
5298
-}
5299
-impl<'de> serde::Deserialize<'de> for PropertyKey {
5300
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
5301
- where
5302
- D: serde::Deserializer<'de>,
5303
- {
5304
- let tagged = serde::Deserializer::deserialize_any(
5305
- deserializer,
5306
- serde::__private::de::TaggedContentVisitor::<
5307
- __PropertyKeyTag,
5308
- >::new("type", "PropertyKey"),
5309
- )?;
5310
- match tagged.0 {
5311
- __PropertyKeyTag::Identifier => {
5312
- let node: Box<Identifier> = <Box<
5313
- Identifier,
5314
- > as Deserialize>::deserialize(
5315
- serde::__private::de::ContentDeserializer::<D::Error>::new(tagged.1),
5316
- )?;
5317
- Ok(PropertyKey::Identifier(node))
5318
- }
5319
- __PropertyKeyTag::Literal => {
5320
- let node: Box<Literal> = <Box<
5321
- Literal,
5322
- > as Deserialize>::deserialize(
5323
- serde::__private::de::ContentDeserializer::<D::Error>::new(tagged.1),
5324
- )?;
5325
- Ok(PropertyKey::Literal(node))
5326
- }
5327
- }
5328
- }
5329
-}
5330
-#[derive(Serialize, Clone, Debug)]
5331
-#[serde(untagged)]
5297
pub enum AssignmentTarget {
5298
Expression(Expression),
5299
Pattern(Pattern),
5300
}
5301
#[derive(Deserialize, Debug)]
5302
enum __AssignmentTargetTag {
5303
+ Identifier,
5304
+ ArrayPattern,
5305
+ ObjectPattern,
5306
+ RestElement,
5307
+ AssignmentPattern,
5308
ArrayExpression,
5309
ArrowFunctionExpression,
5310
AssignmentExpression,
@@ -5347,7 +5317,6 @@ enum __AssignmentTargetTag {
5317
ConditionalExpression,
5318
CoverTypedIdentifier,
5319
FunctionExpression,
5350
- Identifier,
5320
ImportExpression,
5321
JSXElement,
5322
JSXFragment,
@@ -5370,10 +5339,6 @@ enum __AssignmentTargetTag {
5339
UnaryExpression,
5340
UpdateExpression,
5341
YieldExpression,
5373
- ArrayPattern,
5374
- ObjectPattern,
5375
- RestElement,
5376
- AssignmentPattern,
5342
}
5343
impl<'de> serde::Deserialize<'de> for AssignmentTarget {
5344
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
@@ -5387,6 +5352,46 @@ impl<'de> serde::Deserialize<'de> for AssignmentTarget {
5352
>::new("type", "AssignmentTarget"),
5353
)?;
5354
match tagged.0 {
5355
+ __AssignmentTargetTag::Identifier => {
5356
+ let node: Box<Identifier> = <Box<
5357
+ Identifier,
5358
+ > as Deserialize>::deserialize(
5359
+ serde::__private::de::ContentDeserializer::<D::Error>::new(tagged.1),
5360
+ )?;
5361
+ Ok(AssignmentTarget::Pattern(Pattern::Identifier(node)))
5362
+ }
5363
+ __AssignmentTargetTag::ArrayPattern => {
5364
+ let node: Box<ArrayPattern> = <Box<
5365
+ ArrayPattern,
5366
+ > as Deserialize>::deserialize(
5367
+ serde::__private::de::ContentDeserializer::<D::Error>::new(tagged.1),
5368
+ )?;
5369
+ Ok(AssignmentTarget::Pattern(Pattern::ArrayPattern(node)))
5370
+ }
5371
+ __AssignmentTargetTag::ObjectPattern => {
5372
+ let node: Box<ObjectPattern> = <Box<
5373
+ ObjectPattern,
5374
+ > as Deserialize>::deserialize(
5375
+ serde::__private::de::ContentDeserializer::<D::Error>::new(tagged.1),
5376
+ )?;
5377
+ Ok(AssignmentTarget::Pattern(Pattern::ObjectPattern(node)))
5378
+ }
5379
+ __AssignmentTargetTag::RestElement => {
5380
+ let node: Box<RestElement> = <Box<
5381
+ RestElement,
5382
+ > as Deserialize>::deserialize(
5383
+ serde::__private::de::ContentDeserializer::<D::Error>::new(tagged.1),
5384
+ )?;
5385
+ Ok(AssignmentTarget::Pattern(Pattern::RestElement(node)))
5386
+ }
5387
+ __AssignmentTargetTag::AssignmentPattern => {
5388
+ let node: Box<AssignmentPattern> = <Box<
5389
+ AssignmentPattern,
5390
+ > as Deserialize>::deserialize(
5391
+ serde::__private::de::ContentDeserializer::<D::Error>::new(tagged.1),
5392
+ )?;
5393
+ Ok(AssignmentTarget::Pattern(Pattern::AssignmentPattern(node)))
5394
+ }
5395
__AssignmentTargetTag::ArrayExpression => {
5396
let node: Box<ArrayExpression> = <Box<
5397
ArrayExpression,
@@ -5487,14 +5492,6 @@ impl<'de> serde::Deserialize<'de> for AssignmentTarget {
5492
)?;
5493
Ok(AssignmentTarget::Expression(Expression::FunctionExpression(node)))
5494
}
5490
- __AssignmentTargetTag::Identifier => {
5491
- let node: Box<Identifier> = <Box<
5492
- Identifier,
5493
- > as Deserialize>::deserialize(
5494
- serde::__private::de::ContentDeserializer::<D::Error>::new(tagged.1),
5495
- )?;
5496
- Ok(AssignmentTarget::Expression(Expression::Identifier(node)))
5497
- }
5495
__AssignmentTargetTag::ImportExpression => {
5496
let node: Box<ImportExpression> = <Box<
5497
ImportExpression,
@@ -5683,38 +5680,6 @@ impl<'de> serde::Deserialize<'de> for AssignmentTarget {
5680
)?;
5681
Ok(AssignmentTarget::Expression(Expression::YieldExpression(node)))
5682
}
5686
- __AssignmentTargetTag::ArrayPattern => {
5687
- let node: Box<ArrayPattern> = <Box<
5688
- ArrayPattern,
5689
- > as Deserialize>::deserialize(
5690
- serde::__private::de::ContentDeserializer::<D::Error>::new(tagged.1),
5691
- )?;
5692
- Ok(AssignmentTarget::Pattern(Pattern::ArrayPattern(node)))
5693
- }
5694
- __AssignmentTargetTag::ObjectPattern => {
5695
- let node: Box<ObjectPattern> = <Box<
5696
- ObjectPattern,
5697
- > as Deserialize>::deserialize(
5698
- serde::__private::de::ContentDeserializer::<D::Error>::new(tagged.1),
5699
- )?;
5700
- Ok(AssignmentTarget::Pattern(Pattern::ObjectPattern(node)))
5701
- }
5702
- __AssignmentTargetTag::RestElement => {
5703
- let node: Box<RestElement> = <Box<
5704
- RestElement,
5705
- > as Deserialize>::deserialize(
5706
- serde::__private::de::ContentDeserializer::<D::Error>::new(tagged.1),
5707
- )?;
5708
- Ok(AssignmentTarget::Pattern(Pattern::RestElement(node)))
5709
- }
5710
- __AssignmentTargetTag::AssignmentPattern => {
5711
- let node: Box<AssignmentPattern> = <Box<
5712
- AssignmentPattern,
5713
- > as Deserialize>::deserialize(
5714
- serde::__private::de::ContentDeserializer::<D::Error>::new(tagged.1),
5715
- )?;
5716
- Ok(AssignmentTarget::Pattern(Pattern::AssignmentPattern(node)))
5717
- }
5683
}
5684
}
5685
}
@@ -8464,7 +8429,7 @@ pub trait Visitor2 {
8429
}
8430
}
8431
fn visit_assignment_property(&mut self, ast: &AssignmentProperty) {
8467
- self.visit_property_key(&ast.key);
8432
+ self.visit_expression(&ast.key);
8433
self.visit_pattern(&ast.value);
8434
}
8435
fn visit_rest_element(&mut self, ast: &RestElement) {
@@ -8884,24 +8849,14 @@ pub trait Visitor2 {
8849
}
8850
}
8851
}
8887
- fn visit_property_key(&mut self, ast: &PropertyKey) {
8888
- match ast {
8889
- PropertyKey::Identifier(ast) => {
8890
- self.visit_identifier(ast);
8891
- }
8892
- PropertyKey::Literal(ast) => {
8893
- self.visit_literal(ast);
8894
- }
8895
- }
8896
- }
8852
fn visit_assignment_target(&mut self, ast: &AssignmentTarget) {
8853
match ast {
8899
- AssignmentTarget::Expression(ast) => {
8900
- self.visit_expression(ast);
8901
- }
8854
AssignmentTarget::Pattern(ast) => {
8855
self.visit_pattern(ast);
8856
}
8857
+ AssignmentTarget::Expression(ast) => {
8858
+ self.visit_expression(ast);
8859
+ }
8860
}
8861
}
8862
fn visit_chain_element(&mut self, ast: &ChainElement) {
compiler/forget/crates/forget_estree/src/generated_extensions.rs
+1
-1
@@ -6,7 +6,7 @@ pub trait ESTreeNode {}
6
7
impl Default for SourceType {
8
fn default() -> Self {
9
- Self::Script
9
+ Self::Module
10
}
11
}
12
compiler/forget/crates/forget_estree/src/snapshots/forget_estree__tests__fixtures@simple.json.snap
+1
-1
@@ -360,7 +360,7 @@ Output:
360
}
361
}
362
],
363
- "sourceType": "script",
363
+ "sourceType": "module",
364
"loc": {
365
"source": null,
366
"start": {
compiler/forget/crates/forget_estree_codegen/src/ecmascript.json
+13
-8
@@ -879,7 +879,7 @@
879
"type": "Property",
880
"fields": {
881
"key": {
882
- "type": "PropertyKey"
882
+ "type": "Expression"
883
},
884
"value": {
885
"type": "Pattern"
@@ -888,8 +888,17 @@
888
"type": "PropertyKind",
889
"TODO": "fixed value `init`"
890
},
891
- "method": {
891
+ "is_computed": {
892
+ "type": "bool",
893
+ "rename": "computed"
894
+ },
895
+ "is_shorthand": {
896
"type": "bool",
897
+ "rename": "shorthand"
898
+ },
899
+ "is_method": {
900
+ "type": "bool",
901
+ "rename": "method",
902
"TODO": "fixed value `false`"
903
}
904
}
@@ -1210,13 +1219,9 @@
1219
"AssignmentProperty",
1220
"RestElement"
1221
],
1213
- "PropertyKey": [
1214
- "Identifier",
1215
- "Literal"
1216
- ],
1222
"AssignmentTarget": [
1218
- "Expression",
1219
- "Pattern"
1223
+ "Pattern",
1224
+ "Expression"
1225
],
1226
"ChainElement": [
1227
"CallExpression",
compiler/forget/crates/forget_hermes_parser/src/generated.rs
+17
-33
@@ -3034,26 +3034,30 @@ impl FromHermes for AssignmentPropertyOrRestElement {
3034
}
3035
}
3036
}
3037
-impl FromHermes for PropertyKey {
3037
+impl FromHermes for AssignmentTarget {
3038
fn convert(cx: &mut Context, node: NodePtr) -> Self {
3039
let node_ref = node.as_ref();
3040
match node_ref.kind {
3041
NodeKind::Identifier => {
3042
let node = Identifier::convert(cx, node);
3043
- PropertyKey::Identifier(Box::new(node))
3043
+ AssignmentTarget::Pattern(Pattern::Identifier(Box::new(node)))
3044
}
3045
- _ => {
3046
- panic!(
3047
- "Unexpected node kind `{:?}` for `{}`", node_ref.kind, "PropertyKey"
3048
- )
3045
+ NodeKind::ArrayPattern => {
3046
+ let node = ArrayPattern::convert(cx, node);
3047
+ AssignmentTarget::Pattern(Pattern::ArrayPattern(Box::new(node)))
3048
+ }
3049
+ NodeKind::ObjectPattern => {
3050
+ let node = ObjectPattern::convert(cx, node);
3051
+ AssignmentTarget::Pattern(Pattern::ObjectPattern(Box::new(node)))
3052
+ }
3053
+ NodeKind::RestElement => {
3054
+ let node = RestElement::convert(cx, node);
3055
+ AssignmentTarget::Pattern(Pattern::RestElement(Box::new(node)))
3056
+ }
3057
+ NodeKind::AssignmentPattern => {
3058
+ let node = AssignmentPattern::convert(cx, node);
3059
+ AssignmentTarget::Pattern(Pattern::AssignmentPattern(Box::new(node)))
3060
}
3050
- }
3051
- }
3052
-}
3053
-impl FromHermes for AssignmentTarget {
3054
- fn convert(cx: &mut Context, node: NodePtr) -> Self {
3055
- let node_ref = node.as_ref();
3056
- match node_ref.kind {
3061
NodeKind::ArrayExpression => {
3062
let node = ArrayExpression::convert(cx, node);
3063
AssignmentTarget::Expression(Expression::ArrayExpression(Box::new(node)))
@@ -3110,10 +3114,6 @@ impl FromHermes for AssignmentTarget {
3114
Expression::FunctionExpression(Box::new(node)),
3115
)
3116
}
3113
- NodeKind::Identifier => {
3114
- let node = Identifier::convert(cx, node);
3115
- AssignmentTarget::Expression(Expression::Identifier(Box::new(node)))
3116
- }
3117
NodeKind::ImportExpression => {
3118
let node = ImportExpression::convert(cx, node);
3119
AssignmentTarget::Expression(
@@ -3216,22 +3216,6 @@ impl FromHermes for AssignmentTarget {
3216
let node = YieldExpression::convert(cx, node);
3217
AssignmentTarget::Expression(Expression::YieldExpression(Box::new(node)))
3218
}
3219
- NodeKind::ArrayPattern => {
3220
- let node = ArrayPattern::convert(cx, node);
3221
- AssignmentTarget::Pattern(Pattern::ArrayPattern(Box::new(node)))
3222
- }
3223
- NodeKind::ObjectPattern => {
3224
- let node = ObjectPattern::convert(cx, node);
3225
- AssignmentTarget::Pattern(Pattern::ObjectPattern(Box::new(node)))
3226
- }
3227
- NodeKind::RestElement => {
3228
- let node = RestElement::convert(cx, node);
3229
- AssignmentTarget::Pattern(Pattern::RestElement(Box::new(node)))
3230
- }
3231
- NodeKind::AssignmentPattern => {
3232
- let node = AssignmentPattern::convert(cx, node);
3233
- AssignmentTarget::Pattern(Pattern::AssignmentPattern(Box::new(node)))
3234
- }
3219
_ => {
3220
panic!(
3221
"Unexpected node kind `{:?}` for `{}`", node_ref.kind,
compiler/forget/crates/forget_hermes_parser/src/generated_extension.rs
+10
-5
@@ -17,9 +17,10 @@ use hermes::parser::{
17
hermes_get_FunctionDeclaration_id, hermes_get_FunctionDeclaration_params,
18
hermes_get_FunctionExpression_async, hermes_get_FunctionExpression_body,
19
hermes_get_FunctionExpression_generator, hermes_get_FunctionExpression_id,
20
- hermes_get_FunctionExpression_params, hermes_get_Property_key, hermes_get_Property_kind,
21
- hermes_get_Property_method, hermes_get_Property_value, NodeKind, NodeLabel, NodeLabelOpt,
22
- NodeListRef, NodePtr, NodePtrOpt, NodeString, NodeStringOpt, SMRange,
20
+ hermes_get_FunctionExpression_params, hermes_get_Property_computed, hermes_get_Property_key,
21
+ hermes_get_Property_kind, hermes_get_Property_method, hermes_get_Property_shorthand,
22
+ hermes_get_Property_value, NodeKind, NodeLabel, NodeLabelOpt, NodeListRef, NodePtr, NodePtrOpt,
23
+ NodeString, NodeStringOpt, SMRange,
24
};
25
use hermes::utf::utf8_with_surrogates_to_string;
26
@@ -137,14 +138,18 @@ impl FromHermes for AssignmentProperty {
138
let key = FromHermes::convert(cx, unsafe { hermes_get_Property_key(node) });
139
let value = FromHermes::convert(cx, unsafe { hermes_get_Property_value(node) });
140
let kind = FromHermesLabel::convert(cx, unsafe { hermes_get_Property_kind(node) });
140
- let method = unsafe { hermes_get_Property_method(node) };
141
+ let is_method = unsafe { hermes_get_Property_method(node) };
142
+ let is_computed = unsafe { hermes_get_Property_computed(node) };
143
+ let is_shorthand = unsafe { hermes_get_Property_shorthand(node) };
144
let loc = None;
145
let range = convert_range(node);
146
AssignmentProperty {
147
key,
148
value,
149
kind,
147
- method,
150
+ is_method,
151
+ is_computed,
152
+ is_shorthand,
153
loc,
154
range: Some(range),
155
}
compiler/forget/crates/forget_hermes_parser/tests/parser_test.rs
+4
-1
@@ -1,5 +1,6 @@
1
use std::env;
2
3
+use forget_estree::SourceType;
4
use forget_hermes_parser::parse;
5
use insta::{assert_snapshot, glob};
6
use serde_json;
@@ -9,7 +10,9 @@ fn fixtures() {
10
glob!("fixtures/**.js", |path| {
11
println!("fixture {}", path.to_str().unwrap());
12
let input = std::fs::read_to_string(path).unwrap();
12
- let ast = parse(&input, path.to_str().unwrap()).unwrap();
13
+ let mut ast = parse(&input, path.to_str().unwrap()).unwrap();
14
+ // TODO: hack to prevent changing lots of fixtures all at once
15
+ ast.source_type = SourceType::Script;
16
let output = serde_json::to_string_pretty(&ast).unwrap();
17
let output = output.trim();
18
assert_snapshot!(format!("Input:\n{input}\n\nOutput:\n{output}"));
compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@bug.useMemo-deps-array-not-cleared.js.snap
+4
@@ -58,6 +58,8 @@ Output:
58
}
59
},
60
"kind": "init",
61
+ "computed": false,
62
+ "shorthand": true,
63
"method": false,
64
"loc": null,
65
"range": {
@@ -88,6 +90,8 @@ Output:
90
}
91
},
92
"kind": "init",
93
+ "computed": false,
94
+ "shorthand": true,
95
"method": false,
96
"loc": null,
97
"range": {
compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@bug_object-pattern.js.snap
+2
@@ -74,6 +74,8 @@ Output:
74
}
75
},
76
"kind": "init",
77
+ "computed": false,
78
+ "shorthand": true,
79
"method": false,
80
"loc": null,
81
"range": {
compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@capturing-function-member-expr-call.js.snap
+2
@@ -60,6 +60,8 @@ Output:
60
}
61
},
62
"kind": "init",
63
+ "computed": false,
64
+ "shorthand": true,
65
"method": false,
66
"loc": null,
67
"range": {
compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructure-direct-reassignment.js.snap
+4
@@ -124,6 +124,8 @@ Output:
124
}
125
},
126
"kind": "init",
127
+ "computed": false,
128
+ "shorthand": true,
129
"method": false,
130
"loc": null,
131
"range": {
@@ -154,6 +156,8 @@ Output:
156
}
157
},
158
"kind": "init",
159
+ "computed": false,
160
+ "shorthand": true,
161
"method": false,
162
"loc": null,
163
"range": {
compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring-assignment.js.snap
+10
@@ -224,6 +224,8 @@ Output:
224
}
225
},
226
"kind": "init",
227
+ "computed": false,
228
+ "shorthand": false,
229
"method": false,
230
"loc": null,
231
"range": {
@@ -239,6 +241,8 @@ Output:
241
}
242
},
243
"kind": "init",
244
+ "computed": false,
245
+ "shorthand": false,
246
"method": false,
247
"loc": null,
248
"range": {
@@ -356,6 +360,8 @@ Output:
360
}
361
},
362
"kind": "init",
363
+ "computed": false,
364
+ "shorthand": false,
365
"method": false,
366
"loc": null,
367
"range": {
@@ -371,6 +377,8 @@ Output:
377
}
378
},
379
"kind": "init",
380
+ "computed": false,
381
+ "shorthand": false,
382
"method": false,
383
"loc": null,
384
"range": {
@@ -401,6 +409,8 @@ Output:
409
}
410
},
411
"kind": "init",
412
+ "computed": false,
413
+ "shorthand": true,
414
"method": false,
415
"loc": null,
416
"range": {
compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring-mixed-scope-and-local-variables-with-default.js.snap
+6
@@ -263,6 +263,8 @@ Output:
263
}
264
},
265
"kind": "init",
266
+ "computed": false,
267
+ "shorthand": true,
268
"method": false,
269
"loc": null,
270
"range": {
@@ -310,6 +312,8 @@ Output:
312
}
313
},
314
"kind": "init",
315
+ "computed": false,
316
+ "shorthand": true,
317
"method": false,
318
"loc": null,
319
"range": {
@@ -357,6 +361,8 @@ Output:
361
}
362
},
363
"kind": "init",
364
+ "computed": false,
365
+ "shorthand": true,
366
"method": false,
367
"loc": null,
368
"range": {
compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring-mixed-scope-declarations-and-locals.js.snap
+6
@@ -247,6 +247,8 @@ Output:
247
}
248
},
249
"kind": "init",
250
+ "computed": false,
251
+ "shorthand": true,
252
"method": false,
253
"loc": null,
254
"range": {
@@ -277,6 +279,8 @@ Output:
279
}
280
},
281
"kind": "init",
282
+ "computed": false,
283
+ "shorthand": true,
284
"method": false,
285
"loc": null,
286
"range": {
@@ -307,6 +311,8 @@ Output:
311
}
312
},
313
"kind": "init",
314
+ "computed": false,
315
+ "shorthand": true,
316
"method": false,
317
"loc": null,
318
"range": {
compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring-object-default.js.snap
+4
@@ -90,6 +90,8 @@ Output:
90
}
91
},
92
"kind": "init",
93
+ "computed": false,
94
+ "shorthand": true,
95
"method": false,
96
"loc": null,
97
"range": {
@@ -152,6 +154,8 @@ Output:
154
}
155
},
156
"kind": "init",
157
+ "computed": false,
158
+ "shorthand": false,
159
"method": false,
160
"loc": null,
161
"range": {
compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring-object-param-default.js.snap
+2
@@ -69,6 +69,8 @@ Output:
69
}
70
},
71
"kind": "init",
72
+ "computed": false,
73
+ "shorthand": true,
74
"method": false,
75
"loc": null,
76
"range": {
compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring-property-inference.js.snap
+2
@@ -191,6 +191,8 @@ Output:
191
}
192
},
193
"kind": "init",
194
+ "computed": false,
195
+ "shorthand": false,
196
"method": false,
197
"loc": null,
198
"range": {
compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring.js.snap
+10
@@ -139,6 +139,8 @@ Output:
139
}
140
},
141
"kind": "init",
142
+ "computed": false,
143
+ "shorthand": true,
144
"method": false,
145
"loc": null,
146
"range": {
@@ -154,6 +156,8 @@ Output:
156
}
157
},
158
"kind": "init",
159
+ "computed": false,
160
+ "shorthand": false,
161
"method": false,
162
"loc": null,
163
"range": {
@@ -326,6 +330,8 @@ Output:
330
}
331
},
332
"kind": "init",
333
+ "computed": false,
334
+ "shorthand": false,
335
"method": false,
336
"loc": null,
337
"range": {
@@ -341,6 +347,8 @@ Output:
347
}
348
},
349
"kind": "init",
350
+ "computed": false,
351
+ "shorthand": false,
352
"method": false,
353
"loc": null,
354
"range": {
@@ -371,6 +379,8 @@ Output:
379
}
380
},
381
"kind": "init",
382
+ "computed": false,
383
+ "shorthand": true,
384
"method": false,
385
"loc": null,
386
"range": {
compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@error._todo.computed-lval-in-destructure.js.snap
+2
@@ -133,6 +133,8 @@ Output:
133
}
134
},
135
"kind": "init",
136
+ "computed": true,
137
+ "shorthand": false,
138
"method": false,
139
"loc": null,
140
"range": {
compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@error.invalid-use-ref-added-to-dep-without-type-info.js.snap
+2
@@ -61,6 +61,8 @@ Output:
61
}
62
},
63
"kind": "init",
64
+ "computed": false,
65
+ "shorthand": true,
66
"method": false,
67
"loc": null,
68
"range": {
compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@error.todo-kitchensink.js.snap
+12
@@ -149,6 +149,8 @@ Output:
149
}
150
},
151
"kind": "init",
152
+ "computed": false,
153
+ "shorthand": true,
154
"method": false,
155
"loc": null,
156
"range": {
@@ -179,6 +181,8 @@ Output:
181
}
182
},
183
"kind": "init",
184
+ "computed": false,
185
+ "shorthand": true,
186
"method": false,
187
"loc": null,
188
"range": {
@@ -226,6 +230,8 @@ Output:
230
}
231
},
232
"kind": "init",
233
+ "computed": false,
234
+ "shorthand": true,
235
"method": false,
236
"loc": null,
237
"range": {
@@ -721,6 +727,8 @@ Output:
727
}
728
},
729
"kind": "init",
730
+ "computed": false,
731
+ "shorthand": true,
732
"method": false,
733
"loc": null,
734
"range": {
@@ -768,6 +776,8 @@ Output:
776
}
777
},
778
"kind": "init",
779
+ "computed": false,
780
+ "shorthand": true,
781
"method": false,
782
"loc": null,
783
"range": {
@@ -1604,6 +1614,8 @@ Output:
1614
}
1615
},
1616
"kind": "init",
1617
+ "computed": false,
1618
+ "shorthand": true,
1619
"method": false,
1620
"loc": null,
1621
"range": {
compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@escape-analysis-destructured-rest-element.js.snap
+2
@@ -76,6 +76,8 @@ Output:
76
}
77
},
78
"kind": "init",
79
+ "computed": false,
80
+ "shorthand": true,
81
"method": false,
82
"loc": null,
83
"range": {
compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@for-of-destructure.js.snap
+2
@@ -269,6 +269,8 @@ Output:
269
}
270
},
271
"kind": "init",
272
+ "computed": false,
273
+ "shorthand": true,
274
"method": false,
275
"loc": null,
276
"range": {
compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@object-pattern-params.js.snap
+4
@@ -54,6 +54,8 @@ Output:
54
}
55
},
56
"kind": "init",
57
+ "computed": false,
58
+ "shorthand": true,
59
"method": false,
60
"loc": null,
61
"range": {
@@ -84,6 +86,8 @@ Output:
86
}
87
},
88
"kind": "init",
89
+ "computed": false,
90
+ "shorthand": true,
91
"method": false,
92
"loc": null,
93
"range": {
compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@ref-current-aliased-not-added-to-dep-2.js.snap
+2
@@ -57,6 +57,8 @@ Output:
57
}
58
},
59
"kind": "init",
60
+ "computed": false,
61
+ "shorthand": true,
62
"method": false,
63
"loc": null,
64
"range": {
compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@ref-current-not-added-to-dep-2.js.snap
+2
@@ -56,6 +56,8 @@ Output:
56
}
57
},
58
"kind": "init",
59
+ "computed": false,
60
+ "shorthand": true,
61
"method": false,
62
"loc": null,
63
"range": {
compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@remove-memoization-kitchen-sink.js.snap
+4
@@ -488,6 +488,8 @@ Output:
488
}
489
},
490
"kind": "init",
491
+ "computed": false,
492
+ "shorthand": true,
493
"method": false,
494
"loc": null,
495
"range": {
@@ -518,6 +520,8 @@ Output:
520
}
521
},
522
"kind": "init",
523
+ "computed": false,
524
+ "shorthand": true,
525
"method": false,
526
"loc": null,
527
"range": {
compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@ssa-renaming-ternary-destruction-with-mutation.js.snap
+2
@@ -232,6 +232,8 @@ Output:
232
}
233
},
234
"kind": "init",
235
+ "computed": false,
236
+ "shorthand": true,
237
"method": false,
238
"loc": null,
239
"range": {
compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@ssa-renaming-ternary-destruction.js.snap
+2
@@ -231,6 +231,8 @@ Output:
231
}
232
},
233
"kind": "init",
234
+ "computed": false,
235
+ "shorthand": true,
236
"method": false,
237
"loc": null,
238
"range": {
compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@ssa-renaming-via-destructuring-with-mutation.js.snap
+6
@@ -80,6 +80,8 @@ Output:
80
}
81
},
82
"kind": "init",
83
+ "computed": false,
84
+ "shorthand": true,
85
"method": false,
86
"loc": null,
87
"range": {
@@ -290,6 +292,8 @@ Output:
292
}
293
},
294
"kind": "init",
295
+ "computed": false,
296
+ "shorthand": true,
297
"method": false,
298
"loc": null,
299
"range": {
@@ -389,6 +393,8 @@ Output:
393
}
394
},
395
"kind": "init",
396
+ "computed": false,
397
+ "shorthand": true,
398
"method": false,
399
"loc": null,
400
"range": {
compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@ssa-renaming-via-destructuring.js.snap
+6
@@ -79,6 +79,8 @@ Output:
79
}
80
},
81
"kind": "init",
82
+ "computed": false,
83
+ "shorthand": true,
84
"method": false,
85
"loc": null,
86
"range": {
@@ -289,6 +291,8 @@ Output:
291
}
292
},
293
"kind": "init",
294
+ "computed": false,
295
+ "shorthand": true,
296
"method": false,
297
"loc": null,
298
"range": {
@@ -388,6 +392,8 @@ Output:
392
}
393
},
394
"kind": "init",
395
+ "computed": false,
396
+ "shorthand": true,
397
"method": false,
398
"loc": null,
399
"range": {
compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@unused-object-element-with-rest.js.snap
+2
@@ -74,6 +74,8 @@ Output:
74
}
75
},
76
"kind": "init",
77
+ "computed": false,
78
+ "shorthand": true,
79
"method": false,
80
"loc": null,
81
"range": {
compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@unused-object-element.js.snap
+4
@@ -73,6 +73,8 @@ Output:
73
}
74
},
75
"kind": "init",
76
+ "computed": false,
77
+ "shorthand": true,
78
"method": false,
79
"loc": null,
80
"range": {
@@ -103,6 +105,8 @@ Output:
105
}
106
},
107
"kind": "init",
108
+ "computed": false,
109
+ "shorthand": true,
110
"method": false,
111
"loc": null,
112
"range": {
compiler/forget/crates/forget_semantic_analysis/src/analyzer.rs
new
+539
@@ -0,0 +1,539 @@
1
+use forget_diagnostics::Diagnostic;
2
+use forget_estree::{
3
+ AssignmentOperator, AssignmentPropertyOrRestElement, AssignmentTarget, Expression,
4
+ ExpressionOrSuper, ForInInit, Function, FunctionBody, Identifier, Pattern, Program,
5
+ SourceRange, SourceType, Statement, VariableDeclarationKind, Visitor2,
6
+};
7
+
8
+use crate::{AstNode, DeclarationKind, LabelKind, ReferenceKind, ScopeId, ScopeKind, ScopeManager};
9
+
10
+pub fn analyze(ast: &Program) -> ScopeManager {
11
+ let mut analyzer = Analyzer::new();
12
+ analyzer.visit_program(ast);
13
+ analyzer.manager
14
+}
15
+
16
+struct Analyzer {
17
+ manager: ScopeManager,
18
+ current: ScopeId,
19
+}
20
+
21
+impl Analyzer {
22
+ fn new() -> Self {
23
+ let manager = ScopeManager::new();
24
+ let current = manager.root_id();
25
+ Self { manager, current }
26
+ }
27
+
28
+ fn enter<F>(&mut self, kind: ScopeKind, mut f: F) -> ScopeId
29
+ where
30
+ F: FnMut(&mut Self) -> (),
31
+ {
32
+ let scope = self.manager.add_scope(self.current, kind);
33
+ let previous = std::mem::replace(&mut self.current, scope);
34
+ f(self);
35
+ let scope = std::mem::replace(&mut self.current, previous);
36
+ scope
37
+ }
38
+
39
+ fn enter_scope(&mut self, kind: ScopeKind) -> ScopeId {
40
+ let scope = self.manager.add_scope(self.current, kind);
41
+ self.current = scope;
42
+ scope
43
+ }
44
+
45
+ fn close_scope(&mut self, id: ScopeId) {
46
+ assert_eq!(self.current, id, "Mismatched enter_scope/close_scope");
47
+ let scope = self.manager.scope(self.current);
48
+ self.current = scope.parent.unwrap();
49
+ }
50
+
51
+ fn visit_function(&mut self, function: &Function) {
52
+ self.enter(ScopeKind::Function, |visitor| {
53
+ for param in &function.params {
54
+ // `this` parameters don't declare variables, nor can they have
55
+ // default values
56
+ if let Pattern::Identifier(param) = param {
57
+ if ¶m.name == "this" {
58
+ continue;
59
+ }
60
+ }
61
+ Analyzer::visit_declaration_pattern(
62
+ visitor,
63
+ param,
64
+ Some(DeclarationKind::FunctionDeclaration),
65
+ );
66
+ }
67
+
68
+ if let Some(body) = &function.body {
69
+ match body {
70
+ FunctionBody::BlockStatement(body) => {
71
+ // Skip calling visit_block_statement to avoid creating an extra
72
+ // block scope
73
+ for item in &body.body {
74
+ visitor.visit_statement(item);
75
+ }
76
+ }
77
+ FunctionBody::Expression(body) => {
78
+ visitor.visit_expression(body);
79
+ }
80
+ }
81
+ }
82
+ });
83
+ }
84
+
85
+ fn visit_reference_identifier(
86
+ &mut self,
87
+ name: &str,
88
+ ast: AstNode,
89
+ kind: ReferenceKind,
90
+ range: Option<SourceRange>,
91
+ ) {
92
+ let declaration = self.manager.lookup_declaration(self.current, name);
93
+ if let Some(declaration) = declaration {
94
+ let id = self
95
+ .manager
96
+ .add_reference(self.current, kind, declaration.id);
97
+ self.manager.node_references.insert(ast, id);
98
+ } else {
99
+ // Oops, undefined variable
100
+ self.manager
101
+ .diagnostics
102
+ .push(Diagnostic::invalid_syntax("Undefined variable", range));
103
+ }
104
+ }
105
+
106
+ fn visit_declaration_identifier(
107
+ &mut self,
108
+ ast: &Identifier,
109
+ decl_kind: Option<DeclarationKind>,
110
+ ) {
111
+ if let Some(decl_kind) = decl_kind {
112
+ // Declaring a "new" variable, report an error if this is a duplicate
113
+ // definition. In either case, we create a new declaration. Ie we
114
+ // act as if shadowing is allowed in the language
115
+ let previous_declaration = self.manager.lookup_declaration(self.current, &ast.name);
116
+ if let Some(previous_declaration) = previous_declaration {
117
+ if previous_declaration.scope == self.current {
118
+ // duplicate definition in the same scope
119
+ self.manager.diagnostics.push(Diagnostic::invalid_syntax(
120
+ "Duplicate declaration",
121
+ ast.range,
122
+ ));
123
+ }
124
+ }
125
+ let id = self
126
+ .manager
127
+ .add_declaration(self.current, ast.name.clone(), decl_kind);
128
+ self.manager
129
+ .node_declarations
130
+ .insert(AstNode::from(ast), id);
131
+ } else {
132
+ // Referencing an existing variable, it should be defined
133
+ if let Some(declaration) = self.manager.lookup_declaration(self.current, &ast.name) {
134
+ let reference = self.manager.add_reference(
135
+ self.current,
136
+ ReferenceKind::ReadWrite,
137
+ declaration.id,
138
+ );
139
+ self.manager
140
+ .node_references
141
+ .insert(AstNode::from(ast), reference);
142
+ } else {
143
+ self.manager
144
+ .diagnostics
145
+ .push(Diagnostic::invalid_syntax("Undefined variable", ast.range));
146
+ }
147
+ }
148
+ }
149
+
150
+ fn visit_declaration_pattern(&mut self, ast: &Pattern, decl_kind: Option<DeclarationKind>) {
151
+ match ast {
152
+ Pattern::Identifier(ast) => {
153
+ self.visit_declaration_identifier(ast, decl_kind);
154
+ }
155
+ Pattern::ArrayPattern(ast) => {
156
+ for pat in &ast.elements {
157
+ if let Some(pat) = pat {
158
+ self.visit_declaration_pattern(pat, decl_kind);
159
+ }
160
+ }
161
+ }
162
+ Pattern::ObjectPattern(ast) => {
163
+ for property in &ast.properties {
164
+ match property {
165
+ AssignmentPropertyOrRestElement::AssignmentProperty(property) => {
166
+ if property.is_computed {
167
+ self.visit_expression(&property.key);
168
+ }
169
+ self.visit_declaration_pattern(&property.value, decl_kind);
170
+ }
171
+ AssignmentPropertyOrRestElement::RestElement(property) => {
172
+ self.visit_declaration_pattern(&property.argument, decl_kind);
173
+ }
174
+ }
175
+ }
176
+ }
177
+ Pattern::RestElement(ast) => {
178
+ self.visit_declaration_pattern(&ast.argument, decl_kind);
179
+ }
180
+ Pattern::AssignmentPattern(ast) => {
181
+ self.visit_expression(&ast.right);
182
+ self.visit_declaration_pattern(&ast.left, decl_kind);
183
+ }
184
+ }
185
+ }
186
+}
187
+
188
+impl Visitor2 for Analyzer {
189
+ fn visit_function_declaration(&mut self, ast: &forget_estree::FunctionDeclaration) {
190
+ if let Some(id) = &ast.function.id {
191
+ let declaration = self.manager.add_declaration(
192
+ self.current,
193
+ id.name.clone(),
194
+ DeclarationKind::FunctionDeclaration,
195
+ );
196
+ self.manager
197
+ .node_declarations
198
+ .insert(AstNode::from(id), declaration);
199
+ }
200
+ Analyzer::visit_function(self, &ast.function);
201
+ }
202
+
203
+ fn visit_function_expression(&mut self, ast: &forget_estree::FunctionExpression) {
204
+ let mut function_scope: Option<ScopeId> = None;
205
+ if let Some(id) = &ast.function.id {
206
+ function_scope = Some(self.enter_scope(ScopeKind::Function));
207
+ let declaration = self.manager.add_declaration(
208
+ self.current,
209
+ id.name.clone(),
210
+ DeclarationKind::FunctionDeclaration,
211
+ );
212
+ self.manager
213
+ .node_declarations
214
+ .insert(AstNode::from(id), declaration);
215
+ }
216
+
217
+ Analyzer::visit_function(self, &ast.function);
218
+ if let Some(function_scope) = function_scope {
219
+ self.close_scope(function_scope);
220
+ }
221
+ }
222
+
223
+ fn visit_arrow_function_expression(&mut self, ast: &forget_estree::ArrowFunctionExpression) {
224
+ Analyzer::visit_function(self, &ast.function);
225
+ }
226
+
227
+ fn visit_assignment_expression(&mut self, ast: &forget_estree::AssignmentExpression) {
228
+ if ast.operator == AssignmentOperator::Equals {
229
+ match &ast.left {
230
+ AssignmentTarget::Pattern(left) => {
231
+ Analyzer::visit_declaration_pattern(self, left, None);
232
+ }
233
+ AssignmentTarget::Expression(left) => match left {
234
+ Expression::MemberExpression(left) => {
235
+ let mut current = left;
236
+ // If this is a chain of member expressions, find the innermost .object
237
+ // If that's an identifier, record it as a Read.
238
+ // Technically we could probably just visit .object normally,
239
+ // but in case we want to change the Read to something else we do this
240
+ // expansion.
241
+ // TODO: revisit and maybe revert this to just visit ast.left normally
242
+ loop {
243
+ if current.is_computed {
244
+ self.visit_expression_or_private_identifier(¤t.property);
245
+ }
246
+ match ¤t.object {
247
+ ExpressionOrSuper::Expression(object) => match object {
248
+ Expression::MemberExpression(object) => {
249
+ current = object;
250
+ }
251
+ Expression::Identifier(object) => {
252
+ Analyzer::visit_reference_identifier(
253
+ self,
254
+ &object.name,
255
+ AstNode::from(object.as_ref()),
256
+ ReferenceKind::Read,
257
+ object.range,
258
+ );
259
+ break;
260
+ }
261
+ _ => {
262
+ self.visit_expression(object);
263
+ break;
264
+ }
265
+ },
266
+ ExpressionOrSuper::Super(object) => {
267
+ self.visit_super(object);
268
+ break;
269
+ }
270
+ }
271
+ }
272
+ }
273
+ _ => {
274
+ self.manager.diagnostics.push(Diagnostic::invalid_syntax(
275
+ "Invalid AssignmentExpression, expected left-hand side to be a Pattern or MemberExpression",
276
+ ast.range
277
+ ));
278
+ }
279
+ },
280
+ }
281
+ self.visit_expression(&ast.right);
282
+ } else {
283
+ let left: &Identifier;
284
+ if let AssignmentTarget::Pattern(pat) = &ast.left {
285
+ if let Pattern::Identifier(pat) = pat {
286
+ left = pat;
287
+ } else {
288
+ self.manager.diagnostics.push(Diagnostic::invalid_syntax(
289
+ "Expected AssignmentExpression.left to be an Identifier when using operator {}",
290
+ pat.range()
291
+ ));
292
+ // Visit the right-hand side anyway to find any errors there
293
+ self.visit_expression(&ast.right);
294
+ return;
295
+ }
296
+ } else {
297
+ self.manager.diagnostics.push(Diagnostic::invalid_syntax(
298
+ "Expected AssignmentExpression.left to be an Identifier when using operator {}",
299
+ ast.range,
300
+ ));
301
+ // Visit the right-hand side anyway to find any errors there
302
+ self.visit_expression(&ast.right);
303
+ return;
304
+ }
305
+ Analyzer::visit_reference_identifier(
306
+ self,
307
+ &left.name,
308
+ AstNode::from(left),
309
+ ReferenceKind::ReadWrite,
310
+ left.range,
311
+ );
312
+ self.visit_expression(&ast.right);
313
+ }
314
+ }
315
+
316
+ fn visit_block_statement(&mut self, ast: &forget_estree::BlockStatement) {
317
+ self.enter(ScopeKind::Block, |visitor| {
318
+ for stmt in &ast.body {
319
+ visitor.visit_statement(stmt);
320
+ }
321
+ });
322
+ }
323
+
324
+ fn visit_break_statement(&mut self, ast: &forget_estree::BreakStatement) {
325
+ if let Some(label_node) = &ast.label {
326
+ if let Some(label) = self
327
+ .manager
328
+ .lookup_label(self.current, &label_node.name)
329
+ .cloned()
330
+ {
331
+ self.manager
332
+ .node_labels
333
+ .insert(AstNode::from(ast), label.id);
334
+ self.manager
335
+ .node_labels
336
+ .insert(AstNode::from(label_node), label.id);
337
+ } else {
338
+ self.manager.diagnostics.push(Diagnostic::invalid_syntax(
339
+ "Unknown break label",
340
+ label_node.range,
341
+ ));
342
+ }
343
+ } else {
344
+ if let Some(label) = self.manager.lookup_break(self.current).cloned() {
345
+ self.manager
346
+ .node_labels
347
+ .insert(AstNode::from(ast), label.id);
348
+ } else {
349
+ self.manager
350
+ .diagnostics
351
+ .push(Diagnostic::invalid_syntax("Invalid break", ast.range));
352
+ }
353
+ }
354
+ }
355
+
356
+ fn visit_catch_clause(&mut self, ast: &forget_estree::CatchClause) {
357
+ if let Some(param) = &ast.param {
358
+ self.enter(ScopeKind::CatchClause, |visitor| {
359
+ Analyzer::visit_declaration_pattern(
360
+ visitor,
361
+ param,
362
+ Some(DeclarationKind::CatchClause),
363
+ );
364
+ visitor.visit_block_statement(&ast.body);
365
+ });
366
+ } else {
367
+ self.visit_block_statement(&ast.body);
368
+ }
369
+ }
370
+
371
+ fn visit_continue_statement(&mut self, ast: &forget_estree::ContinueStatement) {
372
+ if let Some(label_node) = &ast.label {
373
+ if let Some(label) = self
374
+ .manager
375
+ .lookup_label(self.current, &label_node.name)
376
+ .cloned()
377
+ {
378
+ self.manager
379
+ .node_labels
380
+ .insert(AstNode::from(ast), label.id);
381
+ self.manager
382
+ .node_labels
383
+ .insert(AstNode::from(label_node), label.id);
384
+ } else {
385
+ self.manager.diagnostics.push(Diagnostic::invalid_syntax(
386
+ "Unknown continue label",
387
+ label_node.range,
388
+ ));
389
+ }
390
+ } else {
391
+ if let Some(label) = self.manager.lookup_continue(self.current).cloned() {
392
+ self.manager
393
+ .node_labels
394
+ .insert(AstNode::from(ast), label.id);
395
+ } else {
396
+ self.manager
397
+ .diagnostics
398
+ .push(Diagnostic::invalid_syntax("Invalid continue", ast.range));
399
+ }
400
+ }
401
+ }
402
+
403
+ fn visit_for_in_statement(&mut self, ast: &forget_estree::ForInStatement) {
404
+ // Record an anonymous label for the statement to resolve unlabeled break/continue
405
+ let label = self
406
+ .manager
407
+ .add_anonymous_label(self.current, LabelKind::Loop);
408
+ self.manager.node_labels.insert(AstNode::from(ast), label);
409
+
410
+ let mut for_scope: Option<ScopeId> = None;
411
+ match &ast.left {
412
+ ForInInit::VariableDeclaration(left) => {
413
+ if left.kind != VariableDeclarationKind::Var {
414
+ for_scope = Some(self.enter_scope(ScopeKind::For));
415
+ }
416
+ self.visit_variable_declaration(left);
417
+ }
418
+ ForInInit::Pattern(left) => {
419
+ Analyzer::visit_declaration_pattern(self, left, None);
420
+ }
421
+ }
422
+ self.visit_expression(&ast.right);
423
+ self.visit_statement(&ast.body);
424
+ if let Some(for_scope) = for_scope {
425
+ self.close_scope(for_scope);
426
+ }
427
+ }
428
+
429
+ fn visit_for_of_statement(&mut self, ast: &forget_estree::ForOfStatement) {
430
+ todo!("ForOfStatement")
431
+ }
432
+
433
+ fn visit_identifier(&mut self, ast: &forget_estree::Identifier) {
434
+ Analyzer::visit_reference_identifier(
435
+ self,
436
+ &ast.name,
437
+ AstNode::from(ast),
438
+ ReferenceKind::Read,
439
+ ast.range,
440
+ );
441
+ }
442
+
443
+ fn visit_jsxidentifier(&mut self, ast: &forget_estree::JSXIdentifier) {
444
+ Analyzer::visit_reference_identifier(
445
+ self,
446
+ &ast.name,
447
+ AstNode::from(ast),
448
+ ReferenceKind::Read,
449
+ ast.range,
450
+ );
451
+ }
452
+
453
+ fn visit_labeled_statement(&mut self, ast: &forget_estree::LabeledStatement) {
454
+ let body = &ast.body;
455
+ let kind = match body {
456
+ Statement::ForStatement(_)
457
+ | Statement::ForInStatement(_)
458
+ | Statement::ForOfStatement(_)
459
+ | Statement::WhileStatement(_)
460
+ | Statement::DoWhileStatement(_) => LabelKind::Loop,
461
+ _ => LabelKind::Other,
462
+ };
463
+ let id = self
464
+ .manager
465
+ .add_label(self.current, kind, ast.label.name.clone());
466
+ self.manager.node_labels.insert(AstNode::from(ast), id);
467
+ self.visit_statement(body);
468
+ }
469
+
470
+ fn visit_member_expression(&mut self, ast: &forget_estree::MemberExpression) {
471
+ self.visit_expression_or_super(&ast.object);
472
+ if ast.is_computed {
473
+ self.visit_expression_or_private_identifier(&ast.property);
474
+ }
475
+ }
476
+
477
+ fn visit_meta_property(&mut self, _ast: &forget_estree::MetaProperty) {
478
+ // no-op, these are all builtins
479
+ }
480
+
481
+ fn visit_private_identifier(&mut self, _ast: &forget_estree::PrivateIdentifier) {
482
+ // no-op, these refere to class properties
483
+ }
484
+
485
+ fn visit_private_name(&mut self, _ast: &forget_estree::PrivateName) {
486
+ // no-op, these refere to class properties
487
+ }
488
+
489
+ fn visit_pattern(&mut self, _ast: &Pattern) {
490
+ // This is an internal compiler error: all paths to a `Pattern` node should have been
491
+ // covered such that this is unreachable:
492
+ // - VariableDeclaration
493
+ // - AssignmentExpression
494
+ // - CatchClause
495
+ unreachable!(
496
+ "visit_pattern should not be called directly, call Analyzer::visit_declaration_pattern() instead"
497
+ )
498
+ }
499
+
500
+ fn visit_program(&mut self, ast: &forget_estree::Program) {
501
+ if ast.source_type == SourceType::Module {
502
+ self.enter(ScopeKind::Module, |visitor| {
503
+ for item in &ast.body {
504
+ visitor.visit_module_item(item);
505
+ }
506
+ });
507
+ } else {
508
+ for item in &ast.body {
509
+ self.visit_module_item(item);
510
+ }
511
+ }
512
+ }
513
+
514
+ fn visit_property(&mut self, ast: &forget_estree::Property) {
515
+ if ast.is_computed {
516
+ self.visit_expression(&ast.key);
517
+ }
518
+ self.visit_expression(&ast.value);
519
+ }
520
+
521
+ fn visit_switch_statement(&mut self, ast: &forget_estree::SwitchStatement) {
522
+ self.visit_expression(&ast.discriminant);
523
+ self.enter(ScopeKind::Switch, |visitor| {
524
+ for case_ in &ast.cases {
525
+ visitor.visit_switch_case(case_);
526
+ }
527
+ });
528
+ }
529
+
530
+ fn visit_variable_declaration(&mut self, ast: &forget_estree::VariableDeclaration) {
531
+ let kind = ast.kind;
532
+ for declaration in &ast.declarations {
533
+ Analyzer::visit_declaration_pattern(self, &declaration.id, Some(kind.into()));
534
+ if let Some(init) = &declaration.init {
535
+ self.visit_expression(init);
536
+ }
537
+ }
538
+ }
539
+}
compiler/forget/crates/forget_semantic_analysis/src/lib.rs
+5
-2
@@ -1,3 +1,6 @@
1
-mod analyze;
1
+mod analyzer;
2
+mod scope_manager;
3
+mod scope_view;
4
3
-pub use analyze::analyze;
5
+pub use analyzer::analyze;
6
+pub use scope_manager::*;
compiler/forget/crates/forget_semantic_analysis/src/scope_manager.rs
renamed
+73
-35
@@ -1,18 +1,20 @@
1
use forget_diagnostics::Diagnostic;
2
use forget_estree::{
3
BreakStatement, ContinueStatement, ESTreeNode, Identifier, LabeledStatement, Program,
4
- Statement, Visitor,
4
+ Statement, VariableDeclarationKind, Visitor,
5
};
6
use forget_utils::PointerAddress;
7
use indexmap::IndexMap;
8
9
-pub fn analyze(ast: &Program) -> SemanticAnalysis {
9
+use crate::scope_view::ScopeView;
10
+
11
+pub fn analyze(ast: &Program) -> ScopeManager {
12
let mut analyzer = Analyzer::new();
13
analyzer.visit_program(ast);
14
analyzer.results
15
}
16
15
-pub struct SemanticAnalysis {
17
+pub struct ScopeManager {
18
root: ScopeId,
19
20
// Storage of the semantic information
@@ -23,27 +25,15 @@ pub struct SemanticAnalysis {
25
26
// Mapping of AST nodes (by pointer address) to semantic information
27
// Not all nodes will have all types of information available
26
- node_scopes: IndexMap<AstNode, ScopeId>,
27
- node_labels: IndexMap<AstNode, LabelId>,
28
- node_declarations: IndexMap<AstNode, DeclarationId>,
29
- node_references: IndexMap<AstNode, ReferenceId>,
30
- diagnostics: Vec<Diagnostic>,
31
-}
32
-
33
-#[derive(Debug)]
34
-#[allow(dead_code)]
35
-pub struct SemanticAnalysisDebug<'a> {
36
- root: ScopeId,
37
-
38
- // Storage of the semantic information
39
- scopes: &'a Vec<Scope>,
40
- labels: &'a Vec<Label>,
41
- declarations: &'a Vec<Declaration>,
42
- references: &'a Vec<Reference>,
28
+ pub(crate) node_scopes: IndexMap<AstNode, ScopeId>,
29
+ pub(crate) node_labels: IndexMap<AstNode, LabelId>,
30
+ pub(crate) node_declarations: IndexMap<AstNode, DeclarationId>,
31
+ pub(crate) node_references: IndexMap<AstNode, ReferenceId>,
32
+ pub(crate) diagnostics: Vec<Diagnostic>,
33
}
34
45
-impl SemanticAnalysis {
46
- fn new() -> Self {
35
+impl ScopeManager {
36
+ pub(crate) fn new() -> Self {
37
let root_id = ScopeId(0);
38
Self {
39
root: root_id,
@@ -67,13 +57,11 @@ impl SemanticAnalysis {
57
}
58
}
59
70
- pub fn debug(&self) -> SemanticAnalysisDebug<'_> {
71
- SemanticAnalysisDebug {
72
- root: self.root,
73
- scopes: &self.scopes,
74
- labels: &self.labels,
75
- declarations: &self.declarations,
76
- references: &self.references,
60
+ pub fn debug(&self) -> ScopeView<'_> {
61
+ let root = self.root();
62
+ ScopeView {
63
+ manager: self,
64
+ scope: root,
65
}
66
}
67
@@ -93,7 +81,7 @@ impl SemanticAnalysis {
81
&self.declarations[id.0]
82
}
83
96
- pub fn reference(&self, id: ScopeId) -> &Reference {
84
+ pub fn reference(&self, id: ReferenceId) -> &Reference {
85
&self.references[id.0]
86
}
87
@@ -147,6 +135,14 @@ impl SemanticAnalysis {
135
}
136
}
137
138
+ pub fn lookup_break(&self, scope: ScopeId) -> Option<&Label> {
139
+ todo!()
140
+ }
141
+
142
+ pub fn lookup_continue(&self, scope: ScopeId) -> Option<&Label> {
143
+ todo!()
144
+ }
145
+
146
pub fn lookup_declaration(&self, scope: ScopeId, name: &str) -> Option<&Declaration> {
147
let mut current = &self.scopes[scope.0];
148
loop {
@@ -187,6 +183,14 @@ impl SemanticAnalysis {
183
id
184
}
185
186
+ pub(crate) fn add_anonymous_label(&mut self, scope: ScopeId, kind: LabelKind) -> LabelId {
187
+ let id = LabelId(self.labels.len());
188
+ let name = format!("#{}", id.0);
189
+ self.labels.push(Label { id, kind, scope });
190
+ self.scopes[scope.0].labels.insert(name, id);
191
+ id
192
+ }
193
+
194
pub(crate) fn add_declaration(
195
&mut self,
196
scope: ScopeId,
@@ -194,7 +198,12 @@ impl SemanticAnalysis {
198
kind: DeclarationKind,
199
) -> DeclarationId {
200
let id = DeclarationId(self.declarations.len());
197
- self.declarations.push(Declaration { id, kind, scope });
201
+ self.declarations.push(Declaration {
202
+ id,
203
+ kind,
204
+ name: name.clone(),
205
+ scope,
206
+ });
207
self.scopes[scope.0].declarations.insert(name, id);
208
id
209
}
@@ -232,9 +241,13 @@ pub struct LabelId(usize);
241
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone)]
242
pub enum ScopeKind {
243
Global,
244
+ Module,
245
Function,
246
Class,
247
Block,
248
+ For,
249
+ Switch,
250
+ CatchClause,
251
}
252
253
#[derive(Debug, Clone)]
@@ -266,12 +279,26 @@ pub enum DeclarationKind {
279
Const,
280
Var,
281
Let,
282
+ FunctionDeclaration,
283
+ For,
284
+ CatchClause,
285
+}
286
+
287
+impl From<VariableDeclarationKind> for DeclarationKind {
288
+ fn from(value: VariableDeclarationKind) -> Self {
289
+ match value {
290
+ VariableDeclarationKind::Const => Self::Const,
291
+ VariableDeclarationKind::Let => Self::Let,
292
+ VariableDeclarationKind::Var => Self::Var,
293
+ }
294
+ }
295
}
296
297
#[derive(Debug, Clone)]
298
pub struct Declaration {
299
pub id: DeclarationId,
300
pub kind: DeclarationKind,
301
+ pub name: String,
302
pub scope: ScopeId,
303
}
304
@@ -291,7 +318,7 @@ pub struct Reference {
318
}
319
320
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
294
-struct AstNode(PointerAddress);
321
+pub(crate) struct AstNode(PointerAddress);
322
323
impl AstNode {
324
fn new<T: ESTreeNode>(node: &T) -> Self {
@@ -318,14 +345,14 @@ where
345
}
346
347
struct Analyzer {
321
- results: SemanticAnalysis,
348
+ results: ScopeManager,
349
current: ScopeId,
350
is_lvalue: bool,
351
}
352
353
impl Analyzer {
354
fn new() -> Self {
328
- let results = SemanticAnalysis::new();
355
+ let results = ScopeManager::new();
356
let current = results.root_id();
357
Self {
358
results,
@@ -334,7 +361,7 @@ impl Analyzer {
361
}
362
}
363
337
- fn enter<F>(&mut self, kind: ScopeKind, mut f: F) -> ScopeId
364
+ pub(crate) fn enter<F>(&mut self, kind: ScopeKind, mut f: F) -> ScopeId
365
where
366
F: FnMut(&mut Self) -> (),
367
{
@@ -344,6 +371,17 @@ impl Analyzer {
371
let scope = std::mem::replace(&mut self.current, previous);
372
scope
373
}
374
+
375
+ pub(crate) fn enter_scope(&mut self, kind: ScopeKind) -> ScopeId {
376
+ let scope = self.results.add_scope(self.current, kind);
377
+ self.current = scope;
378
+ scope
379
+ }
380
+
381
+ pub(crate) fn close_scope(&mut self) {
382
+ let scope = self.results.scope(self.current);
383
+ self.current = scope.parent.unwrap();
384
+ }
385
}
386
387
impl<'ast> Visitor<'ast> for Analyzer {
compiler/forget/crates/forget_semantic_analysis/src/scope_view.rs
new
+115
@@ -0,0 +1,115 @@
1
+use indexmap::IndexMap;
2
+
3
+use crate::{Declaration, Label, Reference, Scope, ScopeManager};
4
+
5
+pub struct ScopeView<'m> {
6
+ pub(crate) manager: &'m ScopeManager,
7
+ pub(crate) scope: &'m Scope,
8
+}
9
+
10
+impl<'m> std::fmt::Debug for ScopeView<'m> {
11
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
12
+ let labels: IndexMap<_, _> = self
13
+ .scope
14
+ .labels
15
+ .iter()
16
+ .map(|(name, label)| {
17
+ (
18
+ name.clone(),
19
+ LabelView {
20
+ manager: &self.manager,
21
+ label: self.manager.label(*label),
22
+ },
23
+ )
24
+ })
25
+ .collect();
26
+ let declarations: IndexMap<_, _> = self
27
+ .scope
28
+ .declarations
29
+ .iter()
30
+ .map(|(name, declaration)| {
31
+ (
32
+ name.clone(),
33
+ DeclarationView {
34
+ manager: &self.manager,
35
+ declaration: self.manager.declaration(*declaration),
36
+ },
37
+ )
38
+ })
39
+ .collect();
40
+ let references: Vec<_> = self
41
+ .scope
42
+ .references
43
+ .iter()
44
+ .map(|reference| ReferenceView {
45
+ manager: &self.manager,
46
+ reference: self.manager.reference(*reference),
47
+ })
48
+ .collect();
49
+ let children: Vec<_> = self
50
+ .scope
51
+ .children
52
+ .iter()
53
+ .map(|child| ScopeView {
54
+ manager: &self.manager,
55
+ scope: self.manager.scope(*child),
56
+ })
57
+ .collect();
58
+ f.debug_struct("Scope")
59
+ .field("id", &self.scope.id)
60
+ .field("kind", &self.scope.kind)
61
+ .field("labels", &labels)
62
+ .field("declarations", &declarations)
63
+ .field("references", &references)
64
+ .field("children", &children)
65
+ .finish()
66
+ }
67
+}
68
+
69
+pub struct LabelView<'m> {
70
+ manager: &'m ScopeManager,
71
+ label: &'m Label,
72
+}
73
+
74
+impl<'m> std::fmt::Debug for LabelView<'m> {
75
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76
+ f.debug_struct("Label")
77
+ .field("id", &self.label.id)
78
+ .field("kind", &self.label.kind)
79
+ .field("scope", &self.label.scope)
80
+ .finish()
81
+ }
82
+}
83
+
84
+pub struct DeclarationView<'m> {
85
+ manager: &'m ScopeManager,
86
+ declaration: &'m Declaration,
87
+}
88
+
89
+impl<'m> std::fmt::Debug for DeclarationView<'m> {
90
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91
+ f.debug_struct("Declaration")
92
+ .field("id", &self.declaration.id)
93
+ .field("kind", &self.declaration.kind)
94
+ .field("scope", &self.declaration.scope)
95
+ .finish()
96
+ }
97
+}
98
+
99
+pub struct ReferenceView<'m> {
100
+ manager: &'m ScopeManager,
101
+ reference: &'m Reference,
102
+}
103
+
104
+impl<'m> std::fmt::Debug for ReferenceView<'m> {
105
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106
+ let declaration = self.manager.declaration(self.reference.declaration);
107
+ f.debug_struct("Reference")
108
+ .field("id", &self.reference.id)
109
+ .field("kind", &self.reference.kind)
110
+ .field("declaration", &self.reference.declaration)
111
+ .field("declaration (name)", &declaration.name)
112
+ .field("scope", &self.reference.scope)
113
+ .finish()
114
+ }
115
+}
compiler/forget/crates/forget_semantic_analysis/tests/snapshots/analysis_test__fixtures.snap
+202
-256
@@ -436,7 +436,7 @@ AST:
436
}
437
}
438
],
439
- "sourceType": "script",
439
+ "sourceType": "module",
440
"loc": null,
441
"range": {
442
"start": 0,
@@ -445,271 +445,217 @@ AST:
445
}
446
447
Analysis:
448
-SemanticAnalysisDebug {
449
- root: ScopeId(
448
+Scope {
449
+ id: ScopeId(
450
0,
451
),
452
- scopes: [
453
- Scope {
454
- id: ScopeId(
455
- 0,
456
- ),
457
- kind: Global,
458
- parent: None,
459
- labels: {},
460
- declarations: {},
461
- references: [],
462
- children: [
463
- ScopeId(
464
- 1,
465
- ),
466
- ],
467
- },
452
+ kind: Global,
453
+ labels: {},
454
+ declarations: {},
455
+ references: [],
456
+ children: [
457
Scope {
458
id: ScopeId(
459
1,
460
),
472
- kind: Function,
473
- parent: Some(
474
- ScopeId(
475
- 0,
476
- ),
477
- ),
478
- labels: {
479
- "foo": LabelId(
480
- 0,
481
- ),
482
- "bar": LabelId(
483
- 1,
484
- ),
485
- },
461
+ kind: Module,
462
+ labels: {},
463
declarations: {
487
- "props": DeclarationId(
488
- 0,
489
- ),
490
- "y": DeclarationId(
491
- 1,
492
- ),
493
- "x": DeclarationId(
494
- 2,
495
- ),
464
+ "Component": Declaration {
465
+ id: DeclarationId(
466
+ 0,
467
+ ),
468
+ kind: FunctionDeclaration,
469
+ scope: ScopeId(
470
+ 1,
471
+ ),
472
+ },
473
},
497
- references: [
498
- ReferenceId(
499
- 0,
500
- ),
501
- ReferenceId(
502
- 1,
503
- ),
504
- ReferenceId(
505
- 6,
506
- ),
507
- ],
508
- children: [
509
- ScopeId(
510
- 2,
511
- ),
512
- ScopeId(
513
- 4,
514
- ),
515
- ],
516
- },
517
- Scope {
518
- id: ScopeId(
519
- 2,
520
- ),
521
- kind: Block,
522
- parent: Some(
523
- ScopeId(
524
- 1,
525
- ),
526
- ),
527
- labels: {},
528
- declarations: {},
529
- references: [
530
- ReferenceId(
531
- 2,
532
- ),
533
- ReferenceId(
534
- 3,
535
- ),
536
- ReferenceId(
537
- 4,
538
- ),
539
- ReferenceId(
540
- 5,
541
- ),
542
- ],
474
+ references: [],
475
children: [
544
- ScopeId(
545
- 3,
546
- ),
476
+ Scope {
477
+ id: ScopeId(
478
+ 2,
479
+ ),
480
+ kind: Function,
481
+ labels: {
482
+ "foo": Label {
483
+ id: LabelId(
484
+ 0,
485
+ ),
486
+ kind: Loop,
487
+ scope: ScopeId(
488
+ 2,
489
+ ),
490
+ },
491
+ "bar": Label {
492
+ id: LabelId(
493
+ 1,
494
+ ),
495
+ kind: Other,
496
+ scope: ScopeId(
497
+ 2,
498
+ ),
499
+ },
500
+ },
501
+ declarations: {
502
+ "props": Declaration {
503
+ id: DeclarationId(
504
+ 1,
505
+ ),
506
+ kind: FunctionDeclaration,
507
+ scope: ScopeId(
508
+ 2,
509
+ ),
510
+ },
511
+ "y": Declaration {
512
+ id: DeclarationId(
513
+ 2,
514
+ ),
515
+ kind: Let,
516
+ scope: ScopeId(
517
+ 2,
518
+ ),
519
+ },
520
+ "x": Declaration {
521
+ id: DeclarationId(
522
+ 3,
523
+ ),
524
+ kind: Let,
525
+ scope: ScopeId(
526
+ 2,
527
+ ),
528
+ },
529
+ },
530
+ references: [
531
+ Reference {
532
+ id: ReferenceId(
533
+ 0,
534
+ ),
535
+ kind: Read,
536
+ declaration: DeclarationId(
537
+ 3,
538
+ ),
539
+ declaration (name): "x",
540
+ scope: ScopeId(
541
+ 2,
542
+ ),
543
+ },
544
+ Reference {
545
+ id: ReferenceId(
546
+ 1,
547
+ ),
548
+ kind: Read,
549
+ declaration: DeclarationId(
550
+ 3,
551
+ ),
552
+ declaration (name): "x",
553
+ scope: ScopeId(
554
+ 2,
555
+ ),
556
+ },
557
+ Reference {
558
+ id: ReferenceId(
559
+ 6,
560
+ ),
561
+ kind: Read,
562
+ declaration: DeclarationId(
563
+ 1,
564
+ ),
565
+ declaration (name): "props",
566
+ scope: ScopeId(
567
+ 2,
568
+ ),
569
+ },
570
+ ],
571
+ children: [
572
+ Scope {
573
+ id: ScopeId(
574
+ 3,
575
+ ),
576
+ kind: Block,
577
+ labels: {},
578
+ declarations: {},
579
+ references: [
580
+ Reference {
581
+ id: ReferenceId(
582
+ 2,
583
+ ),
584
+ kind: Read,
585
+ declaration: DeclarationId(
586
+ 3,
587
+ ),
588
+ declaration (name): "x",
589
+ scope: ScopeId(
590
+ 3,
591
+ ),
592
+ },
593
+ Reference {
594
+ id: ReferenceId(
595
+ 3,
596
+ ),
597
+ kind: ReadWrite,
598
+ declaration: DeclarationId(
599
+ 2,
600
+ ),
601
+ declaration (name): "y",
602
+ scope: ScopeId(
603
+ 3,
604
+ ),
605
+ },
606
+ Reference {
607
+ id: ReferenceId(
608
+ 4,
609
+ ),
610
+ kind: Read,
611
+ declaration: DeclarationId(
612
+ 3,
613
+ ),
614
+ declaration (name): "x",
615
+ scope: ScopeId(
616
+ 3,
617
+ ),
618
+ },
619
+ Reference {
620
+ id: ReferenceId(
621
+ 5,
622
+ ),
623
+ kind: Read,
624
+ declaration: DeclarationId(
625
+ 2,
626
+ ),
627
+ declaration (name): "y",
628
+ scope: ScopeId(
629
+ 3,
630
+ ),
631
+ },
632
+ ],
633
+ children: [
634
+ Scope {
635
+ id: ScopeId(
636
+ 4,
637
+ ),
638
+ kind: Block,
639
+ labels: {},
640
+ declarations: {},
641
+ references: [],
642
+ children: [],
643
+ },
644
+ ],
645
+ },
646
+ Scope {
647
+ id: ScopeId(
648
+ 5,
649
+ ),
650
+ kind: Block,
651
+ labels: {},
652
+ declarations: {},
653
+ references: [],
654
+ children: [],
655
+ },
656
+ ],
657
+ },
658
],
659
},
549
- Scope {
550
- id: ScopeId(
551
- 3,
552
- ),
553
- kind: Block,
554
- parent: Some(
555
- ScopeId(
556
- 2,
557
- ),
558
- ),
559
- labels: {},
560
- declarations: {},
561
- references: [],
562
- children: [],
563
- },
564
- Scope {
565
- id: ScopeId(
566
- 4,
567
- ),
568
- kind: Block,
569
- parent: Some(
570
- ScopeId(
571
- 1,
572
- ),
573
- ),
574
- labels: {},
575
- declarations: {},
576
- references: [],
577
- children: [],
578
- },
579
- ],
580
- labels: [
581
- Label {
582
- id: LabelId(
583
- 0,
584
- ),
585
- kind: Loop,
586
- scope: ScopeId(
587
- 1,
588
- ),
589
- },
590
- Label {
591
- id: LabelId(
592
- 1,
593
- ),
594
- kind: Other,
595
- scope: ScopeId(
596
- 1,
597
- ),
598
- },
599
- ],
600
- declarations: [
601
- Declaration {
602
- id: DeclarationId(
603
- 0,
604
- ),
605
- kind: Let,
606
- scope: ScopeId(
607
- 1,
608
- ),
609
- },
610
- Declaration {
611
- id: DeclarationId(
612
- 1,
613
- ),
614
- kind: Let,
615
- scope: ScopeId(
616
- 1,
617
- ),
618
- },
619
- Declaration {
620
- id: DeclarationId(
621
- 2,
622
- ),
623
- kind: Let,
624
- scope: ScopeId(
625
- 1,
626
- ),
627
- },
628
- ],
629
- references: [
630
- Reference {
631
- id: ReferenceId(
632
- 0,
633
- ),
634
- kind: Read,
635
- declaration: DeclarationId(
636
- 2,
637
- ),
638
- scope: ScopeId(
639
- 1,
640
- ),
641
- },
642
- Reference {
643
- id: ReferenceId(
644
- 1,
645
- ),
646
- kind: Read,
647
- declaration: DeclarationId(
648
- 2,
649
- ),
650
- scope: ScopeId(
651
- 1,
652
- ),
653
- },
654
- Reference {
655
- id: ReferenceId(
656
- 2,
657
- ),
658
- kind: Read,
659
- declaration: DeclarationId(
660
- 2,
661
- ),
662
- scope: ScopeId(
663
- 2,
664
- ),
665
- },
666
- Reference {
667
- id: ReferenceId(
668
- 3,
669
- ),
670
- kind: ReadWrite,
671
- declaration: DeclarationId(
672
- 1,
673
- ),
674
- scope: ScopeId(
675
- 2,
676
- ),
677
- },
678
- Reference {
679
- id: ReferenceId(
680
- 4,
681
- ),
682
- kind: Read,
683
- declaration: DeclarationId(
684
- 2,
685
- ),
686
- scope: ScopeId(
687
- 2,
688
- ),
689
- },
690
- Reference {
691
- id: ReferenceId(
692
- 5,
693
- ),
694
- kind: Read,
695
- declaration: DeclarationId(
696
- 1,
697
- ),
698
- scope: ScopeId(
699
- 2,
700
- ),
701
- },
702
- Reference {
703
- id: ReferenceId(
704
- 6,
705
- ),
706
- kind: Read,
707
- declaration: DeclarationId(
708
- 0,
709
- ),
710
- scope: ScopeId(
711
- 1,
712
- ),
713
- },
660
],
661
}