diff --git a/compiler/forget/crates/forget_diagnostics/src/diagnostic.rs b/compiler/forget/crates/forget_diagnostics/src/diagnostic.rs index 68c8ee679b..b0b3e19b54 100644 --- a/compiler/forget/crates/forget_diagnostics/src/diagnostic.rs +++ b/compiler/forget/crates/forget_diagnostics/src/diagnostic.rs @@ -272,7 +272,7 @@ impl From for Diagnostics { fn source_span_from_range(range: SourceRange) -> SourceSpan { SourceSpan::new( - ByteOffset::from(range.start as usize - 1).into(), + ByteOffset::from(range.start as usize).into(), ByteOffset::from((u32::from(range.end) - range.start) as usize).into(), ) } diff --git a/compiler/forget/crates/forget_estree/src/generated.rs b/compiler/forget/crates/forget_estree/src/generated.rs index a5dc8e632d..fed2590590 100644 --- a/compiler/forget/crates/forget_estree/src/generated.rs +++ b/compiler/forget/crates/forget_estree/src/generated.rs @@ -1993,10 +1993,15 @@ impl Serialize for ObjectPattern { } #[derive(Deserialize, Clone, Debug)] pub struct AssignmentProperty { - pub key: PropertyKey, + pub key: Expression, pub value: Pattern, pub kind: PropertyKind, - pub method: bool, + #[serde(rename = "computed")] + pub is_computed: bool, + #[serde(rename = "shorthand")] + pub is_shorthand: bool, + #[serde(rename = "method")] + pub is_method: bool, #[serde(default)] pub loc: Option, #[serde(default)] @@ -2013,7 +2018,9 @@ impl Serialize for AssignmentProperty { state.serialize_entry("key", &self.key)?; state.serialize_entry("value", &self.value)?; state.serialize_entry("kind", &self.kind)?; - state.serialize_entry("method", &self.method)?; + state.serialize_entry("computed", &self.is_computed)?; + state.serialize_entry("shorthand", &self.is_shorthand)?; + state.serialize_entry("method", &self.is_method)?; state.serialize_entry("loc", &self.loc)?; state.serialize_entry("range", &self.range)?; state.end() @@ -5287,54 +5294,17 @@ impl<'de> serde::Deserialize<'de> for AssignmentPropertyOrRestElement { } #[derive(Serialize, Clone, Debug)] #[serde(untagged)] -pub enum PropertyKey { - Identifier(Box), - Literal(Box), -} -#[derive(Deserialize, Debug)] -enum __PropertyKeyTag { - Identifier, - Literal, -} -impl<'de> serde::Deserialize<'de> for PropertyKey { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let tagged = serde::Deserializer::deserialize_any( - deserializer, - serde::__private::de::TaggedContentVisitor::< - __PropertyKeyTag, - >::new("type", "PropertyKey"), - )?; - match tagged.0 { - __PropertyKeyTag::Identifier => { - let node: Box = as Deserialize>::deserialize( - serde::__private::de::ContentDeserializer::::new(tagged.1), - )?; - Ok(PropertyKey::Identifier(node)) - } - __PropertyKeyTag::Literal => { - let node: Box = as Deserialize>::deserialize( - serde::__private::de::ContentDeserializer::::new(tagged.1), - )?; - Ok(PropertyKey::Literal(node)) - } - } - } -} -#[derive(Serialize, Clone, Debug)] -#[serde(untagged)] pub enum AssignmentTarget { Expression(Expression), Pattern(Pattern), } #[derive(Deserialize, Debug)] enum __AssignmentTargetTag { + Identifier, + ArrayPattern, + ObjectPattern, + RestElement, + AssignmentPattern, ArrayExpression, ArrowFunctionExpression, AssignmentExpression, @@ -5347,7 +5317,6 @@ enum __AssignmentTargetTag { ConditionalExpression, CoverTypedIdentifier, FunctionExpression, - Identifier, ImportExpression, JSXElement, JSXFragment, @@ -5370,10 +5339,6 @@ enum __AssignmentTargetTag { UnaryExpression, UpdateExpression, YieldExpression, - ArrayPattern, - ObjectPattern, - RestElement, - AssignmentPattern, } impl<'de> serde::Deserialize<'de> for AssignmentTarget { fn deserialize(deserializer: D) -> Result @@ -5387,6 +5352,46 @@ impl<'de> serde::Deserialize<'de> for AssignmentTarget { >::new("type", "AssignmentTarget"), )?; match tagged.0 { + __AssignmentTargetTag::Identifier => { + let node: Box = as Deserialize>::deserialize( + serde::__private::de::ContentDeserializer::::new(tagged.1), + )?; + Ok(AssignmentTarget::Pattern(Pattern::Identifier(node))) + } + __AssignmentTargetTag::ArrayPattern => { + let node: Box = as Deserialize>::deserialize( + serde::__private::de::ContentDeserializer::::new(tagged.1), + )?; + Ok(AssignmentTarget::Pattern(Pattern::ArrayPattern(node))) + } + __AssignmentTargetTag::ObjectPattern => { + let node: Box = as Deserialize>::deserialize( + serde::__private::de::ContentDeserializer::::new(tagged.1), + )?; + Ok(AssignmentTarget::Pattern(Pattern::ObjectPattern(node))) + } + __AssignmentTargetTag::RestElement => { + let node: Box = as Deserialize>::deserialize( + serde::__private::de::ContentDeserializer::::new(tagged.1), + )?; + Ok(AssignmentTarget::Pattern(Pattern::RestElement(node))) + } + __AssignmentTargetTag::AssignmentPattern => { + let node: Box = as Deserialize>::deserialize( + serde::__private::de::ContentDeserializer::::new(tagged.1), + )?; + Ok(AssignmentTarget::Pattern(Pattern::AssignmentPattern(node))) + } __AssignmentTargetTag::ArrayExpression => { let node: Box = serde::Deserialize<'de> for AssignmentTarget { )?; Ok(AssignmentTarget::Expression(Expression::FunctionExpression(node))) } - __AssignmentTargetTag::Identifier => { - let node: Box = as Deserialize>::deserialize( - serde::__private::de::ContentDeserializer::::new(tagged.1), - )?; - Ok(AssignmentTarget::Expression(Expression::Identifier(node))) - } __AssignmentTargetTag::ImportExpression => { let node: Box = serde::Deserialize<'de> for AssignmentTarget { )?; Ok(AssignmentTarget::Expression(Expression::YieldExpression(node))) } - __AssignmentTargetTag::ArrayPattern => { - let node: Box = as Deserialize>::deserialize( - serde::__private::de::ContentDeserializer::::new(tagged.1), - )?; - Ok(AssignmentTarget::Pattern(Pattern::ArrayPattern(node))) - } - __AssignmentTargetTag::ObjectPattern => { - let node: Box = as Deserialize>::deserialize( - serde::__private::de::ContentDeserializer::::new(tagged.1), - )?; - Ok(AssignmentTarget::Pattern(Pattern::ObjectPattern(node))) - } - __AssignmentTargetTag::RestElement => { - let node: Box = as Deserialize>::deserialize( - serde::__private::de::ContentDeserializer::::new(tagged.1), - )?; - Ok(AssignmentTarget::Pattern(Pattern::RestElement(node))) - } - __AssignmentTargetTag::AssignmentPattern => { - let node: Box = as Deserialize>::deserialize( - serde::__private::de::ContentDeserializer::::new(tagged.1), - )?; - Ok(AssignmentTarget::Pattern(Pattern::AssignmentPattern(node))) - } } } } @@ -8464,7 +8429,7 @@ pub trait Visitor2 { } } fn visit_assignment_property(&mut self, ast: &AssignmentProperty) { - self.visit_property_key(&ast.key); + self.visit_expression(&ast.key); self.visit_pattern(&ast.value); } fn visit_rest_element(&mut self, ast: &RestElement) { @@ -8884,24 +8849,14 @@ pub trait Visitor2 { } } } - fn visit_property_key(&mut self, ast: &PropertyKey) { - match ast { - PropertyKey::Identifier(ast) => { - self.visit_identifier(ast); - } - PropertyKey::Literal(ast) => { - self.visit_literal(ast); - } - } - } fn visit_assignment_target(&mut self, ast: &AssignmentTarget) { match ast { - AssignmentTarget::Expression(ast) => { - self.visit_expression(ast); - } AssignmentTarget::Pattern(ast) => { self.visit_pattern(ast); } + AssignmentTarget::Expression(ast) => { + self.visit_expression(ast); + } } } fn visit_chain_element(&mut self, ast: &ChainElement) { diff --git a/compiler/forget/crates/forget_estree/src/generated_extensions.rs b/compiler/forget/crates/forget_estree/src/generated_extensions.rs index c593db989b..b864037b34 100644 --- a/compiler/forget/crates/forget_estree/src/generated_extensions.rs +++ b/compiler/forget/crates/forget_estree/src/generated_extensions.rs @@ -6,7 +6,7 @@ pub trait ESTreeNode {} impl Default for SourceType { fn default() -> Self { - Self::Script + Self::Module } } diff --git a/compiler/forget/crates/forget_estree/src/snapshots/forget_estree__tests__fixtures@simple.json.snap b/compiler/forget/crates/forget_estree/src/snapshots/forget_estree__tests__fixtures@simple.json.snap index b1d3ef551f..096e3a558b 100644 --- a/compiler/forget/crates/forget_estree/src/snapshots/forget_estree__tests__fixtures@simple.json.snap +++ b/compiler/forget/crates/forget_estree/src/snapshots/forget_estree__tests__fixtures@simple.json.snap @@ -360,7 +360,7 @@ Output: } } ], - "sourceType": "script", + "sourceType": "module", "loc": { "source": null, "start": { diff --git a/compiler/forget/crates/forget_estree_codegen/src/ecmascript.json b/compiler/forget/crates/forget_estree_codegen/src/ecmascript.json index 62c8327da8..f88f3e2e60 100644 --- a/compiler/forget/crates/forget_estree_codegen/src/ecmascript.json +++ b/compiler/forget/crates/forget_estree_codegen/src/ecmascript.json @@ -879,7 +879,7 @@ "type": "Property", "fields": { "key": { - "type": "PropertyKey" + "type": "Expression" }, "value": { "type": "Pattern" @@ -888,8 +888,17 @@ "type": "PropertyKind", "TODO": "fixed value `init`" }, - "method": { + "is_computed": { "type": "bool", + "rename": "computed" + }, + "is_shorthand": { + "type": "bool", + "rename": "shorthand" + }, + "is_method": { + "type": "bool", + "rename": "method", "TODO": "fixed value `false`" } } @@ -1210,13 +1219,9 @@ "AssignmentProperty", "RestElement" ], - "PropertyKey": [ - "Identifier", - "Literal" - ], "AssignmentTarget": [ - "Expression", - "Pattern" + "Pattern", + "Expression" ], "ChainElement": [ "CallExpression", diff --git a/compiler/forget/crates/forget_hermes_parser/src/generated.rs b/compiler/forget/crates/forget_hermes_parser/src/generated.rs index 5b97360787..85e23e4c2c 100644 --- a/compiler/forget/crates/forget_hermes_parser/src/generated.rs +++ b/compiler/forget/crates/forget_hermes_parser/src/generated.rs @@ -3034,26 +3034,30 @@ impl FromHermes for AssignmentPropertyOrRestElement { } } } -impl FromHermes for PropertyKey { +impl FromHermes for AssignmentTarget { fn convert(cx: &mut Context, node: NodePtr) -> Self { let node_ref = node.as_ref(); match node_ref.kind { NodeKind::Identifier => { let node = Identifier::convert(cx, node); - PropertyKey::Identifier(Box::new(node)) + AssignmentTarget::Pattern(Pattern::Identifier(Box::new(node))) } - _ => { - panic!( - "Unexpected node kind `{:?}` for `{}`", node_ref.kind, "PropertyKey" - ) + NodeKind::ArrayPattern => { + let node = ArrayPattern::convert(cx, node); + AssignmentTarget::Pattern(Pattern::ArrayPattern(Box::new(node))) + } + NodeKind::ObjectPattern => { + let node = ObjectPattern::convert(cx, node); + AssignmentTarget::Pattern(Pattern::ObjectPattern(Box::new(node))) + } + NodeKind::RestElement => { + let node = RestElement::convert(cx, node); + AssignmentTarget::Pattern(Pattern::RestElement(Box::new(node))) + } + NodeKind::AssignmentPattern => { + let node = AssignmentPattern::convert(cx, node); + AssignmentTarget::Pattern(Pattern::AssignmentPattern(Box::new(node))) } - } - } -} -impl FromHermes for AssignmentTarget { - fn convert(cx: &mut Context, node: NodePtr) -> Self { - let node_ref = node.as_ref(); - match node_ref.kind { NodeKind::ArrayExpression => { let node = ArrayExpression::convert(cx, node); AssignmentTarget::Expression(Expression::ArrayExpression(Box::new(node))) @@ -3110,10 +3114,6 @@ impl FromHermes for AssignmentTarget { Expression::FunctionExpression(Box::new(node)), ) } - NodeKind::Identifier => { - let node = Identifier::convert(cx, node); - AssignmentTarget::Expression(Expression::Identifier(Box::new(node))) - } NodeKind::ImportExpression => { let node = ImportExpression::convert(cx, node); AssignmentTarget::Expression( @@ -3216,22 +3216,6 @@ impl FromHermes for AssignmentTarget { let node = YieldExpression::convert(cx, node); AssignmentTarget::Expression(Expression::YieldExpression(Box::new(node))) } - NodeKind::ArrayPattern => { - let node = ArrayPattern::convert(cx, node); - AssignmentTarget::Pattern(Pattern::ArrayPattern(Box::new(node))) - } - NodeKind::ObjectPattern => { - let node = ObjectPattern::convert(cx, node); - AssignmentTarget::Pattern(Pattern::ObjectPattern(Box::new(node))) - } - NodeKind::RestElement => { - let node = RestElement::convert(cx, node); - AssignmentTarget::Pattern(Pattern::RestElement(Box::new(node))) - } - NodeKind::AssignmentPattern => { - let node = AssignmentPattern::convert(cx, node); - AssignmentTarget::Pattern(Pattern::AssignmentPattern(Box::new(node))) - } _ => { panic!( "Unexpected node kind `{:?}` for `{}`", node_ref.kind, diff --git a/compiler/forget/crates/forget_hermes_parser/src/generated_extension.rs b/compiler/forget/crates/forget_hermes_parser/src/generated_extension.rs index 3dfbb95508..54af5d991f 100644 --- a/compiler/forget/crates/forget_hermes_parser/src/generated_extension.rs +++ b/compiler/forget/crates/forget_hermes_parser/src/generated_extension.rs @@ -17,9 +17,10 @@ use hermes::parser::{ hermes_get_FunctionDeclaration_id, hermes_get_FunctionDeclaration_params, hermes_get_FunctionExpression_async, hermes_get_FunctionExpression_body, hermes_get_FunctionExpression_generator, hermes_get_FunctionExpression_id, - hermes_get_FunctionExpression_params, hermes_get_Property_key, hermes_get_Property_kind, - hermes_get_Property_method, hermes_get_Property_value, NodeKind, NodeLabel, NodeLabelOpt, - NodeListRef, NodePtr, NodePtrOpt, NodeString, NodeStringOpt, SMRange, + hermes_get_FunctionExpression_params, hermes_get_Property_computed, hermes_get_Property_key, + hermes_get_Property_kind, hermes_get_Property_method, hermes_get_Property_shorthand, + hermes_get_Property_value, NodeKind, NodeLabel, NodeLabelOpt, NodeListRef, NodePtr, NodePtrOpt, + NodeString, NodeStringOpt, SMRange, }; use hermes::utf::utf8_with_surrogates_to_string; @@ -137,14 +138,18 @@ impl FromHermes for AssignmentProperty { let key = FromHermes::convert(cx, unsafe { hermes_get_Property_key(node) }); let value = FromHermes::convert(cx, unsafe { hermes_get_Property_value(node) }); let kind = FromHermesLabel::convert(cx, unsafe { hermes_get_Property_kind(node) }); - let method = unsafe { hermes_get_Property_method(node) }; + let is_method = unsafe { hermes_get_Property_method(node) }; + let is_computed = unsafe { hermes_get_Property_computed(node) }; + let is_shorthand = unsafe { hermes_get_Property_shorthand(node) }; let loc = None; let range = convert_range(node); AssignmentProperty { key, value, kind, - method, + is_method, + is_computed, + is_shorthand, loc, range: Some(range), } diff --git a/compiler/forget/crates/forget_hermes_parser/tests/parser_test.rs b/compiler/forget/crates/forget_hermes_parser/tests/parser_test.rs index 345c8e8891..c76b0c5de0 100644 --- a/compiler/forget/crates/forget_hermes_parser/tests/parser_test.rs +++ b/compiler/forget/crates/forget_hermes_parser/tests/parser_test.rs @@ -1,5 +1,6 @@ use std::env; +use forget_estree::SourceType; use forget_hermes_parser::parse; use insta::{assert_snapshot, glob}; use serde_json; @@ -9,7 +10,9 @@ fn fixtures() { glob!("fixtures/**.js", |path| { println!("fixture {}", path.to_str().unwrap()); let input = std::fs::read_to_string(path).unwrap(); - let ast = parse(&input, path.to_str().unwrap()).unwrap(); + let mut ast = parse(&input, path.to_str().unwrap()).unwrap(); + // TODO: hack to prevent changing lots of fixtures all at once + ast.source_type = SourceType::Script; let output = serde_json::to_string_pretty(&ast).unwrap(); let output = output.trim(); assert_snapshot!(format!("Input:\n{input}\n\nOutput:\n{output}")); diff --git a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@bug.useMemo-deps-array-not-cleared.js.snap b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@bug.useMemo-deps-array-not-cleared.js.snap index c0387e11ff..f79804102c 100644 --- a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@bug.useMemo-deps-array-not-cleared.js.snap +++ b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@bug.useMemo-deps-array-not-cleared.js.snap @@ -58,6 +58,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { @@ -88,6 +90,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { diff --git a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@bug_object-pattern.js.snap b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@bug_object-pattern.js.snap index 661e3bb5da..06c24450ba 100644 --- a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@bug_object-pattern.js.snap +++ b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@bug_object-pattern.js.snap @@ -74,6 +74,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { diff --git a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@capturing-function-member-expr-call.js.snap b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@capturing-function-member-expr-call.js.snap index 758ec979bf..38bda4c499 100644 --- a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@capturing-function-member-expr-call.js.snap +++ b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@capturing-function-member-expr-call.js.snap @@ -60,6 +60,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { diff --git a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructure-direct-reassignment.js.snap b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructure-direct-reassignment.js.snap index ca0fd2b8a3..e9bdd31ffe 100644 --- a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructure-direct-reassignment.js.snap +++ b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructure-direct-reassignment.js.snap @@ -124,6 +124,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { @@ -154,6 +156,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { diff --git a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring-assignment.js.snap b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring-assignment.js.snap index f32bbe6671..0337c0bd6a 100644 --- a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring-assignment.js.snap +++ b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring-assignment.js.snap @@ -224,6 +224,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": false, "method": false, "loc": null, "range": { @@ -239,6 +241,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": false, "method": false, "loc": null, "range": { @@ -356,6 +360,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": false, "method": false, "loc": null, "range": { @@ -371,6 +377,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": false, "method": false, "loc": null, "range": { @@ -401,6 +409,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { diff --git a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring-mixed-scope-and-local-variables-with-default.js.snap b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring-mixed-scope-and-local-variables-with-default.js.snap index 083895bab9..4ff8cc6172 100644 --- a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring-mixed-scope-and-local-variables-with-default.js.snap +++ b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring-mixed-scope-and-local-variables-with-default.js.snap @@ -263,6 +263,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { @@ -310,6 +312,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { @@ -357,6 +361,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { diff --git a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring-mixed-scope-declarations-and-locals.js.snap b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring-mixed-scope-declarations-and-locals.js.snap index 50fcb70941..5c8bab3846 100644 --- a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring-mixed-scope-declarations-and-locals.js.snap +++ b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring-mixed-scope-declarations-and-locals.js.snap @@ -247,6 +247,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { @@ -277,6 +279,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { @@ -307,6 +311,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { diff --git a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring-object-default.js.snap b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring-object-default.js.snap index a6626f4cde..9cb947a92a 100644 --- a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring-object-default.js.snap +++ b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring-object-default.js.snap @@ -90,6 +90,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { @@ -152,6 +154,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": false, "method": false, "loc": null, "range": { diff --git a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring-object-param-default.js.snap b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring-object-param-default.js.snap index bbd2a96507..b85a1bc751 100644 --- a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring-object-param-default.js.snap +++ b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring-object-param-default.js.snap @@ -69,6 +69,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { diff --git a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring-property-inference.js.snap b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring-property-inference.js.snap index cb946891c1..f645cb8b29 100644 --- a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring-property-inference.js.snap +++ b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring-property-inference.js.snap @@ -191,6 +191,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": false, "method": false, "loc": null, "range": { diff --git a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring.js.snap b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring.js.snap index 47a93ecbc7..1d35614976 100644 --- a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring.js.snap +++ b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@destructuring.js.snap @@ -139,6 +139,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { @@ -154,6 +156,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": false, "method": false, "loc": null, "range": { @@ -326,6 +330,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": false, "method": false, "loc": null, "range": { @@ -341,6 +347,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": false, "method": false, "loc": null, "range": { @@ -371,6 +379,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { diff --git a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@error._todo.computed-lval-in-destructure.js.snap b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@error._todo.computed-lval-in-destructure.js.snap index b4d0f0c22e..951139fa12 100644 --- a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@error._todo.computed-lval-in-destructure.js.snap +++ b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@error._todo.computed-lval-in-destructure.js.snap @@ -133,6 +133,8 @@ Output: } }, "kind": "init", + "computed": true, + "shorthand": false, "method": false, "loc": null, "range": { diff --git a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@error.invalid-use-ref-added-to-dep-without-type-info.js.snap b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@error.invalid-use-ref-added-to-dep-without-type-info.js.snap index 8c5581ee80..f688842047 100644 --- a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@error.invalid-use-ref-added-to-dep-without-type-info.js.snap +++ b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@error.invalid-use-ref-added-to-dep-without-type-info.js.snap @@ -61,6 +61,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { diff --git a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@error.todo-kitchensink.js.snap b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@error.todo-kitchensink.js.snap index e9d4ae0956..0da2b0676e 100644 --- a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@error.todo-kitchensink.js.snap +++ b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@error.todo-kitchensink.js.snap @@ -149,6 +149,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { @@ -179,6 +181,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { @@ -226,6 +230,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { @@ -721,6 +727,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { @@ -768,6 +776,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { @@ -1604,6 +1614,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { diff --git a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@escape-analysis-destructured-rest-element.js.snap b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@escape-analysis-destructured-rest-element.js.snap index 4d615c7c8e..8c48711622 100644 --- a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@escape-analysis-destructured-rest-element.js.snap +++ b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@escape-analysis-destructured-rest-element.js.snap @@ -76,6 +76,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { diff --git a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@for-of-destructure.js.snap b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@for-of-destructure.js.snap index a99dcd502c..7508308225 100644 --- a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@for-of-destructure.js.snap +++ b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@for-of-destructure.js.snap @@ -269,6 +269,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { diff --git a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@object-pattern-params.js.snap b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@object-pattern-params.js.snap index f121e9f058..d62de0077c 100644 --- a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@object-pattern-params.js.snap +++ b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@object-pattern-params.js.snap @@ -54,6 +54,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { @@ -84,6 +86,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { diff --git a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@ref-current-aliased-not-added-to-dep-2.js.snap b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@ref-current-aliased-not-added-to-dep-2.js.snap index f4255a8f0e..775f3ca9fb 100644 --- a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@ref-current-aliased-not-added-to-dep-2.js.snap +++ b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@ref-current-aliased-not-added-to-dep-2.js.snap @@ -57,6 +57,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { diff --git a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@ref-current-not-added-to-dep-2.js.snap b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@ref-current-not-added-to-dep-2.js.snap index 61a05685e1..0a2d4dfec7 100644 --- a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@ref-current-not-added-to-dep-2.js.snap +++ b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@ref-current-not-added-to-dep-2.js.snap @@ -56,6 +56,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { diff --git a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@remove-memoization-kitchen-sink.js.snap b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@remove-memoization-kitchen-sink.js.snap index 32a6fcefc8..7231865df5 100644 --- a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@remove-memoization-kitchen-sink.js.snap +++ b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@remove-memoization-kitchen-sink.js.snap @@ -488,6 +488,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { @@ -518,6 +520,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { diff --git a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@ssa-renaming-ternary-destruction-with-mutation.js.snap b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@ssa-renaming-ternary-destruction-with-mutation.js.snap index 797e3d699f..f8fd81a8db 100644 --- a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@ssa-renaming-ternary-destruction-with-mutation.js.snap +++ b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@ssa-renaming-ternary-destruction-with-mutation.js.snap @@ -232,6 +232,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { diff --git a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@ssa-renaming-ternary-destruction.js.snap b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@ssa-renaming-ternary-destruction.js.snap index 1542d0d847..5d193655cb 100644 --- a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@ssa-renaming-ternary-destruction.js.snap +++ b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@ssa-renaming-ternary-destruction.js.snap @@ -231,6 +231,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { diff --git a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@ssa-renaming-via-destructuring-with-mutation.js.snap b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@ssa-renaming-via-destructuring-with-mutation.js.snap index e8e05f63b8..8f2a8aa0d1 100644 --- a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@ssa-renaming-via-destructuring-with-mutation.js.snap +++ b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@ssa-renaming-via-destructuring-with-mutation.js.snap @@ -80,6 +80,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { @@ -290,6 +292,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { @@ -389,6 +393,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { diff --git a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@ssa-renaming-via-destructuring.js.snap b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@ssa-renaming-via-destructuring.js.snap index 208b9a81b3..a2409051fb 100644 --- a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@ssa-renaming-via-destructuring.js.snap +++ b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@ssa-renaming-via-destructuring.js.snap @@ -79,6 +79,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { @@ -289,6 +291,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { @@ -388,6 +392,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { diff --git a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@unused-object-element-with-rest.js.snap b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@unused-object-element-with-rest.js.snap index 4fd4695964..4202a9e418 100644 --- a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@unused-object-element-with-rest.js.snap +++ b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@unused-object-element-with-rest.js.snap @@ -74,6 +74,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { diff --git a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@unused-object-element.js.snap b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@unused-object-element.js.snap index e751a13db9..87a4768cb4 100644 --- a/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@unused-object-element.js.snap +++ b/compiler/forget/crates/forget_hermes_parser/tests/snapshots/parser_test__fixtures@unused-object-element.js.snap @@ -73,6 +73,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { @@ -103,6 +105,8 @@ Output: } }, "kind": "init", + "computed": false, + "shorthand": true, "method": false, "loc": null, "range": { diff --git a/compiler/forget/crates/forget_semantic_analysis/src/analyzer.rs b/compiler/forget/crates/forget_semantic_analysis/src/analyzer.rs new file mode 100644 index 0000000000..201c7a9fbb --- /dev/null +++ b/compiler/forget/crates/forget_semantic_analysis/src/analyzer.rs @@ -0,0 +1,539 @@ +use forget_diagnostics::Diagnostic; +use forget_estree::{ + AssignmentOperator, AssignmentPropertyOrRestElement, AssignmentTarget, Expression, + ExpressionOrSuper, ForInInit, Function, FunctionBody, Identifier, Pattern, Program, + SourceRange, SourceType, Statement, VariableDeclarationKind, Visitor2, +}; + +use crate::{AstNode, DeclarationKind, LabelKind, ReferenceKind, ScopeId, ScopeKind, ScopeManager}; + +pub fn analyze(ast: &Program) -> ScopeManager { + let mut analyzer = Analyzer::new(); + analyzer.visit_program(ast); + analyzer.manager +} + +struct Analyzer { + manager: ScopeManager, + current: ScopeId, +} + +impl Analyzer { + fn new() -> Self { + let manager = ScopeManager::new(); + let current = manager.root_id(); + Self { manager, current } + } + + fn enter(&mut self, kind: ScopeKind, mut f: F) -> ScopeId + where + F: FnMut(&mut Self) -> (), + { + let scope = self.manager.add_scope(self.current, kind); + let previous = std::mem::replace(&mut self.current, scope); + f(self); + let scope = std::mem::replace(&mut self.current, previous); + scope + } + + fn enter_scope(&mut self, kind: ScopeKind) -> ScopeId { + let scope = self.manager.add_scope(self.current, kind); + self.current = scope; + scope + } + + fn close_scope(&mut self, id: ScopeId) { + assert_eq!(self.current, id, "Mismatched enter_scope/close_scope"); + let scope = self.manager.scope(self.current); + self.current = scope.parent.unwrap(); + } + + fn visit_function(&mut self, function: &Function) { + self.enter(ScopeKind::Function, |visitor| { + for param in &function.params { + // `this` parameters don't declare variables, nor can they have + // default values + if let Pattern::Identifier(param) = param { + if ¶m.name == "this" { + continue; + } + } + Analyzer::visit_declaration_pattern( + visitor, + param, + Some(DeclarationKind::FunctionDeclaration), + ); + } + + if let Some(body) = &function.body { + match body { + FunctionBody::BlockStatement(body) => { + // Skip calling visit_block_statement to avoid creating an extra + // block scope + for item in &body.body { + visitor.visit_statement(item); + } + } + FunctionBody::Expression(body) => { + visitor.visit_expression(body); + } + } + } + }); + } + + fn visit_reference_identifier( + &mut self, + name: &str, + ast: AstNode, + kind: ReferenceKind, + range: Option, + ) { + let declaration = self.manager.lookup_declaration(self.current, name); + if let Some(declaration) = declaration { + let id = self + .manager + .add_reference(self.current, kind, declaration.id); + self.manager.node_references.insert(ast, id); + } else { + // Oops, undefined variable + self.manager + .diagnostics + .push(Diagnostic::invalid_syntax("Undefined variable", range)); + } + } + + fn visit_declaration_identifier( + &mut self, + ast: &Identifier, + decl_kind: Option, + ) { + if let Some(decl_kind) = decl_kind { + // Declaring a "new" variable, report an error if this is a duplicate + // definition. In either case, we create a new declaration. Ie we + // act as if shadowing is allowed in the language + let previous_declaration = self.manager.lookup_declaration(self.current, &ast.name); + if let Some(previous_declaration) = previous_declaration { + if previous_declaration.scope == self.current { + // duplicate definition in the same scope + self.manager.diagnostics.push(Diagnostic::invalid_syntax( + "Duplicate declaration", + ast.range, + )); + } + } + let id = self + .manager + .add_declaration(self.current, ast.name.clone(), decl_kind); + self.manager + .node_declarations + .insert(AstNode::from(ast), id); + } else { + // Referencing an existing variable, it should be defined + if let Some(declaration) = self.manager.lookup_declaration(self.current, &ast.name) { + let reference = self.manager.add_reference( + self.current, + ReferenceKind::ReadWrite, + declaration.id, + ); + self.manager + .node_references + .insert(AstNode::from(ast), reference); + } else { + self.manager + .diagnostics + .push(Diagnostic::invalid_syntax("Undefined variable", ast.range)); + } + } + } + + fn visit_declaration_pattern(&mut self, ast: &Pattern, decl_kind: Option) { + match ast { + Pattern::Identifier(ast) => { + self.visit_declaration_identifier(ast, decl_kind); + } + Pattern::ArrayPattern(ast) => { + for pat in &ast.elements { + if let Some(pat) = pat { + self.visit_declaration_pattern(pat, decl_kind); + } + } + } + Pattern::ObjectPattern(ast) => { + for property in &ast.properties { + match property { + AssignmentPropertyOrRestElement::AssignmentProperty(property) => { + if property.is_computed { + self.visit_expression(&property.key); + } + self.visit_declaration_pattern(&property.value, decl_kind); + } + AssignmentPropertyOrRestElement::RestElement(property) => { + self.visit_declaration_pattern(&property.argument, decl_kind); + } + } + } + } + Pattern::RestElement(ast) => { + self.visit_declaration_pattern(&ast.argument, decl_kind); + } + Pattern::AssignmentPattern(ast) => { + self.visit_expression(&ast.right); + self.visit_declaration_pattern(&ast.left, decl_kind); + } + } + } +} + +impl Visitor2 for Analyzer { + fn visit_function_declaration(&mut self, ast: &forget_estree::FunctionDeclaration) { + if let Some(id) = &ast.function.id { + let declaration = self.manager.add_declaration( + self.current, + id.name.clone(), + DeclarationKind::FunctionDeclaration, + ); + self.manager + .node_declarations + .insert(AstNode::from(id), declaration); + } + Analyzer::visit_function(self, &ast.function); + } + + fn visit_function_expression(&mut self, ast: &forget_estree::FunctionExpression) { + let mut function_scope: Option = None; + if let Some(id) = &ast.function.id { + function_scope = Some(self.enter_scope(ScopeKind::Function)); + let declaration = self.manager.add_declaration( + self.current, + id.name.clone(), + DeclarationKind::FunctionDeclaration, + ); + self.manager + .node_declarations + .insert(AstNode::from(id), declaration); + } + + Analyzer::visit_function(self, &ast.function); + if let Some(function_scope) = function_scope { + self.close_scope(function_scope); + } + } + + fn visit_arrow_function_expression(&mut self, ast: &forget_estree::ArrowFunctionExpression) { + Analyzer::visit_function(self, &ast.function); + } + + fn visit_assignment_expression(&mut self, ast: &forget_estree::AssignmentExpression) { + if ast.operator == AssignmentOperator::Equals { + match &ast.left { + AssignmentTarget::Pattern(left) => { + Analyzer::visit_declaration_pattern(self, left, None); + } + AssignmentTarget::Expression(left) => match left { + Expression::MemberExpression(left) => { + let mut current = left; + // If this is a chain of member expressions, find the innermost .object + // If that's an identifier, record it as a Read. + // Technically we could probably just visit .object normally, + // but in case we want to change the Read to something else we do this + // expansion. + // TODO: revisit and maybe revert this to just visit ast.left normally + loop { + if current.is_computed { + self.visit_expression_or_private_identifier(¤t.property); + } + match ¤t.object { + ExpressionOrSuper::Expression(object) => match object { + Expression::MemberExpression(object) => { + current = object; + } + Expression::Identifier(object) => { + Analyzer::visit_reference_identifier( + self, + &object.name, + AstNode::from(object.as_ref()), + ReferenceKind::Read, + object.range, + ); + break; + } + _ => { + self.visit_expression(object); + break; + } + }, + ExpressionOrSuper::Super(object) => { + self.visit_super(object); + break; + } + } + } + } + _ => { + self.manager.diagnostics.push(Diagnostic::invalid_syntax( + "Invalid AssignmentExpression, expected left-hand side to be a Pattern or MemberExpression", + ast.range + )); + } + }, + } + self.visit_expression(&ast.right); + } else { + let left: &Identifier; + if let AssignmentTarget::Pattern(pat) = &ast.left { + if let Pattern::Identifier(pat) = pat { + left = pat; + } else { + self.manager.diagnostics.push(Diagnostic::invalid_syntax( + "Expected AssignmentExpression.left to be an Identifier when using operator {}", + pat.range() + )); + // Visit the right-hand side anyway to find any errors there + self.visit_expression(&ast.right); + return; + } + } else { + self.manager.diagnostics.push(Diagnostic::invalid_syntax( + "Expected AssignmentExpression.left to be an Identifier when using operator {}", + ast.range, + )); + // Visit the right-hand side anyway to find any errors there + self.visit_expression(&ast.right); + return; + } + Analyzer::visit_reference_identifier( + self, + &left.name, + AstNode::from(left), + ReferenceKind::ReadWrite, + left.range, + ); + self.visit_expression(&ast.right); + } + } + + fn visit_block_statement(&mut self, ast: &forget_estree::BlockStatement) { + self.enter(ScopeKind::Block, |visitor| { + for stmt in &ast.body { + visitor.visit_statement(stmt); + } + }); + } + + fn visit_break_statement(&mut self, ast: &forget_estree::BreakStatement) { + if let Some(label_node) = &ast.label { + if let Some(label) = self + .manager + .lookup_label(self.current, &label_node.name) + .cloned() + { + self.manager + .node_labels + .insert(AstNode::from(ast), label.id); + self.manager + .node_labels + .insert(AstNode::from(label_node), label.id); + } else { + self.manager.diagnostics.push(Diagnostic::invalid_syntax( + "Unknown break label", + label_node.range, + )); + } + } else { + if let Some(label) = self.manager.lookup_break(self.current).cloned() { + self.manager + .node_labels + .insert(AstNode::from(ast), label.id); + } else { + self.manager + .diagnostics + .push(Diagnostic::invalid_syntax("Invalid break", ast.range)); + } + } + } + + fn visit_catch_clause(&mut self, ast: &forget_estree::CatchClause) { + if let Some(param) = &ast.param { + self.enter(ScopeKind::CatchClause, |visitor| { + Analyzer::visit_declaration_pattern( + visitor, + param, + Some(DeclarationKind::CatchClause), + ); + visitor.visit_block_statement(&ast.body); + }); + } else { + self.visit_block_statement(&ast.body); + } + } + + fn visit_continue_statement(&mut self, ast: &forget_estree::ContinueStatement) { + if let Some(label_node) = &ast.label { + if let Some(label) = self + .manager + .lookup_label(self.current, &label_node.name) + .cloned() + { + self.manager + .node_labels + .insert(AstNode::from(ast), label.id); + self.manager + .node_labels + .insert(AstNode::from(label_node), label.id); + } else { + self.manager.diagnostics.push(Diagnostic::invalid_syntax( + "Unknown continue label", + label_node.range, + )); + } + } else { + if let Some(label) = self.manager.lookup_continue(self.current).cloned() { + self.manager + .node_labels + .insert(AstNode::from(ast), label.id); + } else { + self.manager + .diagnostics + .push(Diagnostic::invalid_syntax("Invalid continue", ast.range)); + } + } + } + + fn visit_for_in_statement(&mut self, ast: &forget_estree::ForInStatement) { + // Record an anonymous label for the statement to resolve unlabeled break/continue + let label = self + .manager + .add_anonymous_label(self.current, LabelKind::Loop); + self.manager.node_labels.insert(AstNode::from(ast), label); + + let mut for_scope: Option = None; + match &ast.left { + ForInInit::VariableDeclaration(left) => { + if left.kind != VariableDeclarationKind::Var { + for_scope = Some(self.enter_scope(ScopeKind::For)); + } + self.visit_variable_declaration(left); + } + ForInInit::Pattern(left) => { + Analyzer::visit_declaration_pattern(self, left, None); + } + } + self.visit_expression(&ast.right); + self.visit_statement(&ast.body); + if let Some(for_scope) = for_scope { + self.close_scope(for_scope); + } + } + + fn visit_for_of_statement(&mut self, ast: &forget_estree::ForOfStatement) { + todo!("ForOfStatement") + } + + fn visit_identifier(&mut self, ast: &forget_estree::Identifier) { + Analyzer::visit_reference_identifier( + self, + &ast.name, + AstNode::from(ast), + ReferenceKind::Read, + ast.range, + ); + } + + fn visit_jsxidentifier(&mut self, ast: &forget_estree::JSXIdentifier) { + Analyzer::visit_reference_identifier( + self, + &ast.name, + AstNode::from(ast), + ReferenceKind::Read, + ast.range, + ); + } + + fn visit_labeled_statement(&mut self, ast: &forget_estree::LabeledStatement) { + let body = &ast.body; + let kind = match body { + Statement::ForStatement(_) + | Statement::ForInStatement(_) + | Statement::ForOfStatement(_) + | Statement::WhileStatement(_) + | Statement::DoWhileStatement(_) => LabelKind::Loop, + _ => LabelKind::Other, + }; + let id = self + .manager + .add_label(self.current, kind, ast.label.name.clone()); + self.manager.node_labels.insert(AstNode::from(ast), id); + self.visit_statement(body); + } + + fn visit_member_expression(&mut self, ast: &forget_estree::MemberExpression) { + self.visit_expression_or_super(&ast.object); + if ast.is_computed { + self.visit_expression_or_private_identifier(&ast.property); + } + } + + fn visit_meta_property(&mut self, _ast: &forget_estree::MetaProperty) { + // no-op, these are all builtins + } + + fn visit_private_identifier(&mut self, _ast: &forget_estree::PrivateIdentifier) { + // no-op, these refere to class properties + } + + fn visit_private_name(&mut self, _ast: &forget_estree::PrivateName) { + // no-op, these refere to class properties + } + + fn visit_pattern(&mut self, _ast: &Pattern) { + // This is an internal compiler error: all paths to a `Pattern` node should have been + // covered such that this is unreachable: + // - VariableDeclaration + // - AssignmentExpression + // - CatchClause + unreachable!( + "visit_pattern should not be called directly, call Analyzer::visit_declaration_pattern() instead" + ) + } + + fn visit_program(&mut self, ast: &forget_estree::Program) { + if ast.source_type == SourceType::Module { + self.enter(ScopeKind::Module, |visitor| { + for item in &ast.body { + visitor.visit_module_item(item); + } + }); + } else { + for item in &ast.body { + self.visit_module_item(item); + } + } + } + + fn visit_property(&mut self, ast: &forget_estree::Property) { + if ast.is_computed { + self.visit_expression(&ast.key); + } + self.visit_expression(&ast.value); + } + + fn visit_switch_statement(&mut self, ast: &forget_estree::SwitchStatement) { + self.visit_expression(&ast.discriminant); + self.enter(ScopeKind::Switch, |visitor| { + for case_ in &ast.cases { + visitor.visit_switch_case(case_); + } + }); + } + + fn visit_variable_declaration(&mut self, ast: &forget_estree::VariableDeclaration) { + let kind = ast.kind; + for declaration in &ast.declarations { + Analyzer::visit_declaration_pattern(self, &declaration.id, Some(kind.into())); + if let Some(init) = &declaration.init { + self.visit_expression(init); + } + } + } +} diff --git a/compiler/forget/crates/forget_semantic_analysis/src/lib.rs b/compiler/forget/crates/forget_semantic_analysis/src/lib.rs index 67b61b0aa8..f2849d4890 100644 --- a/compiler/forget/crates/forget_semantic_analysis/src/lib.rs +++ b/compiler/forget/crates/forget_semantic_analysis/src/lib.rs @@ -1,3 +1,6 @@ -mod analyze; +mod analyzer; +mod scope_manager; +mod scope_view; -pub use analyze::analyze; +pub use analyzer::analyze; +pub use scope_manager::*; diff --git a/compiler/forget/crates/forget_semantic_analysis/src/analyze.rs b/compiler/forget/crates/forget_semantic_analysis/src/scope_manager.rs similarity index 85% rename from compiler/forget/crates/forget_semantic_analysis/src/analyze.rs rename to compiler/forget/crates/forget_semantic_analysis/src/scope_manager.rs index 80305d1e5e..025ae362b5 100644 --- a/compiler/forget/crates/forget_semantic_analysis/src/analyze.rs +++ b/compiler/forget/crates/forget_semantic_analysis/src/scope_manager.rs @@ -1,18 +1,20 @@ use forget_diagnostics::Diagnostic; use forget_estree::{ BreakStatement, ContinueStatement, ESTreeNode, Identifier, LabeledStatement, Program, - Statement, Visitor, + Statement, VariableDeclarationKind, Visitor, }; use forget_utils::PointerAddress; use indexmap::IndexMap; -pub fn analyze(ast: &Program) -> SemanticAnalysis { +use crate::scope_view::ScopeView; + +pub fn analyze(ast: &Program) -> ScopeManager { let mut analyzer = Analyzer::new(); analyzer.visit_program(ast); analyzer.results } -pub struct SemanticAnalysis { +pub struct ScopeManager { root: ScopeId, // Storage of the semantic information @@ -23,27 +25,15 @@ pub struct SemanticAnalysis { // Mapping of AST nodes (by pointer address) to semantic information // Not all nodes will have all types of information available - node_scopes: IndexMap, - node_labels: IndexMap, - node_declarations: IndexMap, - node_references: IndexMap, - diagnostics: Vec, + pub(crate) node_scopes: IndexMap, + pub(crate) node_labels: IndexMap, + pub(crate) node_declarations: IndexMap, + pub(crate) node_references: IndexMap, + pub(crate) diagnostics: Vec, } -#[derive(Debug)] -#[allow(dead_code)] -pub struct SemanticAnalysisDebug<'a> { - root: ScopeId, - - // Storage of the semantic information - scopes: &'a Vec, - labels: &'a Vec