@samitouri / QOS-React-1 / commits / e6e2d9437e

[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 &param.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(&current.property);
245 + }
246 + match &current.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 }