diff --git a/compiler/forget/crates/forget_estree/src/generated_extensions.rs b/compiler/forget/crates/forget_estree/src/generated_extensions.rs index b864037b34..b436a55518 100644 --- a/compiler/forget/crates/forget_estree/src/generated_extensions.rs +++ b/compiler/forget/crates/forget_estree/src/generated_extensions.rs @@ -1,5 +1,8 @@ // Manual extensions to generated types -use crate::{Pattern, SourceRange, SourceType}; +use crate::{ + JSXElementName, JSXMemberExpression, JSXMemberExpressionOrIdentifier, Pattern, SourceRange, + SourceType, +}; /// Sentinel trait to distinguish AST *node* types pub trait ESTreeNode {} @@ -21,3 +24,22 @@ impl Pattern { } } } + +impl JSXElementName { + pub fn root_name(&self) -> &str { + match self { + Self::JSXIdentifier(name) => &name.name, + Self::JSXMemberExpression(name) => name.root_name(), + Self::JSXNamespacedName(name) => &name.namespace.name, + } + } +} + +impl JSXMemberExpression { + pub fn root_name(&self) -> &str { + match &self.object { + JSXMemberExpressionOrIdentifier::JSXMemberExpression(object) => object.root_name(), + JSXMemberExpressionOrIdentifier::JSXIdentifier(object) => &object.name, + } + } +} diff --git a/compiler/forget/crates/forget_semantic_analysis/src/analyzer.rs b/compiler/forget/crates/forget_semantic_analysis/src/analyzer.rs index 072849aa47..d919a0b130 100644 --- a/compiler/forget/crates/forget_semantic_analysis/src/analyzer.rs +++ b/compiler/forget/crates/forget_semantic_analysis/src/analyzer.rs @@ -1,8 +1,8 @@ use forget_diagnostics::Diagnostic; use forget_estree::{ AssignmentOperator, AssignmentPropertyOrRestElement, AssignmentTarget, Expression, - ExpressionOrSuper, ForInInit, Function, FunctionBody, Identifier, Pattern, Program, - SourceRange, SourceType, Statement, VariableDeclarationKind, Visitor2, + ExpressionOrSuper, ForInInit, ForInit, Function, FunctionBody, Identifier, JSXElementName, + Pattern, Program, SourceRange, SourceType, Statement, VariableDeclarationKind, Visitor2, }; use crate::{AstNode, DeclarationKind, LabelKind, ReferenceKind, ScopeId, ScopeKind, ScopeManager}; @@ -190,7 +190,7 @@ impl Analyzer { left: &ForInInit, right: &Expression, body: &Statement, - range: Option, + _range: Option, ) { // Record an anonymous label for the statement to resolve unlabeled break/continue let label = self @@ -455,17 +455,31 @@ impl Visitor2 for Analyzer { ); } - 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_for_statement(&mut self, ast: &forget_estree::ForStatement) { + let mut for_scope: Option = None; + if let Some(init) = &ast.init { + if let ForInit::VariableDeclaration(init) = init { + if init.kind != VariableDeclarationKind::Var { + for_scope = Some(self.enter_scope(ScopeKind::For)); + } + } + } + if let Some(init) = &ast.init { + self.visit_for_init(init); + } + if let Some(test) = &ast.test { + self.visit_expression(test); + } + if let Some(update) = &ast.update { + self.visit_expression(update); + } + self.visit_statement(&ast.body); + if let Some(for_scope) = for_scope { + self.close_scope(for_scope); + } } - fn visit_jsxidentifier(&mut self, ast: &forget_estree::JSXIdentifier) { + fn visit_identifier(&mut self, ast: &forget_estree::Identifier) { Analyzer::visit_reference_identifier( self, &ast.name, @@ -561,4 +575,82 @@ impl Visitor2 for Analyzer { } } } + + fn visit_jsxattribute(&mut self, ast: &forget_estree::JSXAttribute) { + // NOTE: skip visiting the attribute name, attributes are like non-computed + // object properties where the identifier is not a variable reference + if let Some(value) = &ast.value { + self.visit_jsxattribute_value(value); + } + } + + fn visit_jsxclosing_element(&mut self, _ast: &forget_estree::JSXClosingElement) { + // no-op, should not be counted as a reference + } + + 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_jsxfragment(&mut self, ast: &forget_estree::JSXFragment) { + // TODO: record the pragmas + for child in &ast.children { + self.visit_jsxchild_item(child); + } + } + + fn visit_jsxmember_expression(&mut self, ast: &forget_estree::JSXMemberExpression) { + // NOTE: ignore the 'property' since JSX doesn't support computed properties + self.visit_jsxmember_expression_or_identifier(&ast.object); + } + + fn visit_jsxnamespaced_name(&mut self, ast: &forget_estree::JSXNamespacedName) { + // NOTE: ignore the 'name' since it doesn't refer to a variable + self.visit_jsxidentifier(&ast.namespace); + } + + fn visit_jsxopening_element(&mut self, ast: &forget_estree::JSXOpeningElement) { + // TODO: record jsx pragma if root_name is not an FBT name + let root_name = ast.name.root_name(); + + match &ast.name { + JSXElementName::JSXIdentifier(name) => { + // lowercase names are builtins, only visit if this is a user-defined + // component + if let Some(first) = root_name.chars().next() { + if first == first.to_ascii_uppercase() { + self.visit_jsxidentifier(name); + } + } else { + // TODO: this likely indicates a parse error, since a valid parse + // should never result in an empty JSXIdentifier node. but just in + // case we report this rather than silently fail + self.manager.diagnostics.push(Diagnostic::invalid_syntax( + "Expected JSXOpenintElement.name to be non-empty", + name.range, + )); + } + } + JSXElementName::JSXMemberExpression(name) => { + if root_name != "this" { + self.visit_jsxmember_expression(name); + } + } + JSXElementName::JSXNamespacedName(name) => { + if root_name != "this" { + self.visit_jsxnamespaced_name(name); + } + } + } + + for attribute in &ast.attributes { + self.visit_jsxattribute_or_spread(attribute); + } + } } diff --git a/compiler/forget/crates/forget_semantic_analysis/src/scope_manager.rs b/compiler/forget/crates/forget_semantic_analysis/src/scope_manager.rs index 025ae362b5..960ca78904 100644 --- a/compiler/forget/crates/forget_semantic_analysis/src/scope_manager.rs +++ b/compiler/forget/crates/forget_semantic_analysis/src/scope_manager.rs @@ -65,6 +65,10 @@ impl ScopeManager { } } + pub fn diagnostics(&mut self) -> Vec { + std::mem::take(&mut self.diagnostics) + } + pub fn root(&self) -> &Scope { &self.scopes[self.root.0] } diff --git a/compiler/forget/crates/forget_semantic_analysis/tests/analysis_test.rs b/compiler/forget/crates/forget_semantic_analysis/tests/analysis_test.rs index b8e610429e..37975e127c 100644 --- a/compiler/forget/crates/forget_semantic_analysis/tests/analysis_test.rs +++ b/compiler/forget/crates/forget_semantic_analysis/tests/analysis_test.rs @@ -1,3 +1,5 @@ +use std::fmt::Write; + use forget_hermes_parser::parse; use forget_semantic_analysis::analyze; use insta::{assert_snapshot, glob}; @@ -8,12 +10,17 @@ fn fixtures() { 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 analysis = analyze(&ast); + let mut analysis = analyze(&ast); let ast_output = serde_json::to_string_pretty(&ast).unwrap(); - let analysis_output = format!("{:#?}", analysis.debug()); + let mut output = String::new(); + writeln!(&mut output, "{:#?}", analysis.debug()).unwrap(); + let diagnostics = analysis.diagnostics(); + for diagnostic in diagnostics { + writeln!(&mut output, "{:#?}", diagnostic).unwrap(); + } assert_snapshot!(format!( - "Input:\n{input}\n\nAST:\n{ast_output}\n\nAnalysis:\n{analysis_output}" + "Input:\n{input}\n\nAST:\n{ast_output}\n\nAnalysis:\n{output}" )); }); } diff --git a/compiler/forget/crates/forget_semantic_analysis/tests/fixtures/globals-and-imports.js b/compiler/forget/crates/forget_semantic_analysis/tests/fixtures/globals-and-imports.js new file mode 100644 index 0000000000..58300c76d3 --- /dev/null +++ b/compiler/forget/crates/forget_semantic_analysis/tests/fixtures/globals-and-imports.js @@ -0,0 +1,17 @@ +import Foo from "foo"; +import * as Bar from "bar"; +import { Baz } from "baz"; + +function Component(props) { + let g = global; + let y = new Array(props.count); + let s = String("hello"); + let b = Boolean(true); + let n = Number(0); + let x = Math.min(props.x, props.y); + setTimeout(() => {}, 0); + setInterval(() => {}, 0); + Foo; + Bar; + Baz; +} diff --git a/compiler/forget/crates/forget_semantic_analysis/tests/snapshots/analysis_test__fixtures@globals-and-imports.js.snap b/compiler/forget/crates/forget_semantic_analysis/tests/snapshots/analysis_test__fixtures@globals-and-imports.js.snap new file mode 100644 index 0000000000..d1b94090ca --- /dev/null +++ b/compiler/forget/crates/forget_semantic_analysis/tests/snapshots/analysis_test__fixtures@globals-and-imports.js.snap @@ -0,0 +1,1219 @@ +--- +source: crates/forget_semantic_analysis/tests/analysis_test.rs +expression: "format!(\"Input:\\n{input}\\n\\nAST:\\n{ast_output}\\n\\nAnalysis:\\n{output}\")" +input_file: crates/forget_semantic_analysis/tests/fixtures/globals-and-imports.js +--- +Input: +import Foo from "foo"; +import * as Bar from "bar"; +import { Baz } from "baz"; + +function Component(props) { + let g = global; + let y = new Array(props.count); + let s = String("hello"); + let b = Boolean(true); + let n = Number(0); + let x = Math.min(props.x, props.y); + setTimeout(() => {}, 0); + setInterval(() => {}, 0); + Foo; + Bar; + Baz; +} + + +AST: +{ + "type": "Program", + "body": [ + { + "type": "ImportDeclaration", + "specifiers": [ + { + "type": "ImportDefaultSpecifier", + "local": { + "type": "Identifier", + "name": "Foo", + "typeAnnotation": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + } + ], + "source": { + "type": "StringLiteral", + "value": "foo", + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + { + "type": "ImportDeclaration", + "specifiers": [ + { + "type": "ImportNamespaceSpecifier", + "local": { + "type": "Identifier", + "name": "Bar", + "typeAnnotation": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + } + ], + "source": { + "type": "StringLiteral", + "value": "bar", + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + { + "type": "ImportDeclaration", + "specifiers": [ + { + "type": "ImportSpecifier", + "imported": { + "type": "Identifier", + "name": "Baz", + "typeAnnotation": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "local": { + "type": "Identifier", + "name": "Baz", + "typeAnnotation": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + } + ], + "source": { + "type": "StringLiteral", + "value": "baz", + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + { + "type": "FunctionDeclaration", + "id": { + "type": "Identifier", + "name": "Component", + "typeAnnotation": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "params": [ + { + "type": "Identifier", + "name": "props", + "typeAnnotation": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + } + ], + "body": { + "type": "BlockStatement", + "body": [ + { + "type": "VariableDeclaration", + "kind": "let", + "declarations": [ + { + "type": "VariableDeclarator", + "id": { + "type": "Identifier", + "name": "g", + "typeAnnotation": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "init": { + "type": "Identifier", + "name": "global", + "typeAnnotation": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + } + ], + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + { + "type": "VariableDeclaration", + "kind": "let", + "declarations": [ + { + "type": "VariableDeclarator", + "id": { + "type": "Identifier", + "name": "y", + "typeAnnotation": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "init": { + "type": "NewExpression", + "callee": { + "type": "Identifier", + "name": "Array", + "typeAnnotation": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "arguments": [ + { + "type": "MemberExpression", + "object": { + "type": "Identifier", + "name": "props", + "typeAnnotation": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "property": { + "type": "Identifier", + "name": "count", + "typeAnnotation": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "computed": false, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + } + ], + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + } + ], + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + { + "type": "VariableDeclaration", + "kind": "let", + "declarations": [ + { + "type": "VariableDeclarator", + "id": { + "type": "Identifier", + "name": "s", + "typeAnnotation": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "init": { + "type": "CallExpression", + "callee": { + "type": "Identifier", + "name": "String", + "typeAnnotation": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "arguments": [ + { + "type": "StringLiteral", + "value": "hello", + "loc": null, + "range": { + "start": 0, + "end": 1 + } + } + ], + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + } + ], + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + { + "type": "VariableDeclaration", + "kind": "let", + "declarations": [ + { + "type": "VariableDeclarator", + "id": { + "type": "Identifier", + "name": "b", + "typeAnnotation": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "init": { + "type": "CallExpression", + "callee": { + "type": "Identifier", + "name": "Boolean", + "typeAnnotation": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "arguments": [ + { + "type": "BooleanLiteral", + "value": true, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + } + ], + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + } + ], + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + { + "type": "VariableDeclaration", + "kind": "let", + "declarations": [ + { + "type": "VariableDeclarator", + "id": { + "type": "Identifier", + "name": "n", + "typeAnnotation": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "init": { + "type": "CallExpression", + "callee": { + "type": "Identifier", + "name": "Number", + "typeAnnotation": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "arguments": [ + { + "type": "NumericLiteral", + "value": 0, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + } + ], + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + } + ], + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + { + "type": "VariableDeclaration", + "kind": "let", + "declarations": [ + { + "type": "VariableDeclarator", + "id": { + "type": "Identifier", + "name": "x", + "typeAnnotation": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "init": { + "type": "CallExpression", + "callee": { + "type": "MemberExpression", + "object": { + "type": "Identifier", + "name": "Math", + "typeAnnotation": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "property": { + "type": "Identifier", + "name": "min", + "typeAnnotation": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "computed": false, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "arguments": [ + { + "type": "MemberExpression", + "object": { + "type": "Identifier", + "name": "props", + "typeAnnotation": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "property": { + "type": "Identifier", + "name": "x", + "typeAnnotation": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "computed": false, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + { + "type": "MemberExpression", + "object": { + "type": "Identifier", + "name": "props", + "typeAnnotation": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "property": { + "type": "Identifier", + "name": "y", + "typeAnnotation": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "computed": false, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + } + ], + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + } + ], + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + { + "type": "ExpressionStatement", + "expression": { + "type": "CallExpression", + "callee": { + "type": "Identifier", + "name": "setTimeout", + "typeAnnotation": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "arguments": [ + { + "type": "ArrowFunctionExpression", + "id": null, + "params": [], + "body": { + "type": "BlockStatement", + "body": [], + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "generator": false, + "async": false, + "loc": null, + "range": { + "start": 0, + "end": 1 + }, + "expression": false, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + { + "type": "NumericLiteral", + "value": 0, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + } + ], + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "directive": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + { + "type": "ExpressionStatement", + "expression": { + "type": "CallExpression", + "callee": { + "type": "Identifier", + "name": "setInterval", + "typeAnnotation": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "arguments": [ + { + "type": "ArrowFunctionExpression", + "id": null, + "params": [], + "body": { + "type": "BlockStatement", + "body": [], + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "generator": false, + "async": false, + "loc": null, + "range": { + "start": 0, + "end": 1 + }, + "expression": false, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + { + "type": "NumericLiteral", + "value": 0, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + } + ], + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "directive": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + { + "type": "ExpressionStatement", + "expression": { + "type": "Identifier", + "name": "Foo", + "typeAnnotation": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "directive": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + { + "type": "ExpressionStatement", + "expression": { + "type": "Identifier", + "name": "Bar", + "typeAnnotation": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "directive": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + { + "type": "ExpressionStatement", + "expression": { + "type": "Identifier", + "name": "Baz", + "typeAnnotation": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "directive": null, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + } + ], + "loc": null, + "range": { + "start": 0, + "end": 1 + } + }, + "generator": false, + "async": false, + "loc": null, + "range": { + "start": 0, + "end": 1 + }, + "loc": null, + "range": { + "start": 0, + "end": 1 + } + } + ], + "sourceType": "module", + "loc": null, + "range": { + "start": 0, + "end": 1 + } +} + +Analysis: +Scope { + id: ScopeId( + 0, + ), + kind: Global, + labels: {}, + declarations: {}, + references: [], + children: [ + Scope { + id: ScopeId( + 1, + ), + kind: Module, + labels: {}, + declarations: { + "Component": Declaration { + id: DeclarationId( + 0, + ), + kind: FunctionDeclaration, + scope: ScopeId( + 1, + ), + }, + }, + references: [], + children: [ + Scope { + id: ScopeId( + 2, + ), + kind: Function, + labels: {}, + declarations: { + "props": Declaration { + id: DeclarationId( + 1, + ), + kind: FunctionDeclaration, + scope: ScopeId( + 2, + ), + }, + "g": Declaration { + id: DeclarationId( + 2, + ), + kind: Let, + scope: ScopeId( + 2, + ), + }, + "y": Declaration { + id: DeclarationId( + 3, + ), + kind: Let, + scope: ScopeId( + 2, + ), + }, + "s": Declaration { + id: DeclarationId( + 4, + ), + kind: Let, + scope: ScopeId( + 2, + ), + }, + "b": Declaration { + id: DeclarationId( + 5, + ), + kind: Let, + scope: ScopeId( + 2, + ), + }, + "n": Declaration { + id: DeclarationId( + 6, + ), + kind: Let, + scope: ScopeId( + 2, + ), + }, + "x": Declaration { + id: DeclarationId( + 7, + ), + kind: Let, + scope: ScopeId( + 2, + ), + }, + }, + references: [ + Reference { + id: ReferenceId( + 0, + ), + kind: Read, + declaration: DeclarationId( + 1, + ), + declaration (name): "props", + scope: ScopeId( + 2, + ), + }, + Reference { + id: ReferenceId( + 1, + ), + kind: Read, + declaration: DeclarationId( + 1, + ), + declaration (name): "props", + scope: ScopeId( + 2, + ), + }, + Reference { + id: ReferenceId( + 2, + ), + kind: Read, + declaration: DeclarationId( + 1, + ), + declaration (name): "props", + scope: ScopeId( + 2, + ), + }, + ], + children: [ + Scope { + id: ScopeId( + 3, + ), + kind: Function, + labels: {}, + declarations: {}, + references: [], + children: [], + }, + Scope { + id: ScopeId( + 4, + ), + kind: Function, + labels: {}, + declarations: {}, + references: [], + children: [], + }, + ], + }, + ], + }, + ], +} +Diagnostic( + DiagnosticData { + message: "Undefined variable", + span: Some( + SourceSpan { + offset: SourceOffset( + 0, + ), + length: 1, + }, + ), + related_information: [], + severity: InvalidSyntax, + data: [], + }, +) +Diagnostic( + DiagnosticData { + message: "Undefined variable", + span: Some( + SourceSpan { + offset: SourceOffset( + 0, + ), + length: 1, + }, + ), + related_information: [], + severity: InvalidSyntax, + data: [], + }, +) +Diagnostic( + DiagnosticData { + message: "Undefined variable", + span: Some( + SourceSpan { + offset: SourceOffset( + 0, + ), + length: 1, + }, + ), + related_information: [], + severity: InvalidSyntax, + data: [], + }, +) +Diagnostic( + DiagnosticData { + message: "Undefined variable", + span: Some( + SourceSpan { + offset: SourceOffset( + 0, + ), + length: 1, + }, + ), + related_information: [], + severity: InvalidSyntax, + data: [], + }, +) +Diagnostic( + DiagnosticData { + message: "Undefined variable", + span: Some( + SourceSpan { + offset: SourceOffset( + 0, + ), + length: 1, + }, + ), + related_information: [], + severity: InvalidSyntax, + data: [], + }, +) +Diagnostic( + DiagnosticData { + message: "Undefined variable", + span: Some( + SourceSpan { + offset: SourceOffset( + 0, + ), + length: 1, + }, + ), + related_information: [], + severity: InvalidSyntax, + data: [], + }, +) +Diagnostic( + DiagnosticData { + message: "Undefined variable", + span: Some( + SourceSpan { + offset: SourceOffset( + 0, + ), + length: 1, + }, + ), + related_information: [], + severity: InvalidSyntax, + data: [], + }, +) +Diagnostic( + DiagnosticData { + message: "Undefined variable", + span: Some( + SourceSpan { + offset: SourceOffset( + 0, + ), + length: 1, + }, + ), + related_information: [], + severity: InvalidSyntax, + data: [], + }, +) +Diagnostic( + DiagnosticData { + message: "Undefined variable", + span: Some( + SourceSpan { + offset: SourceOffset( + 0, + ), + length: 1, + }, + ), + related_information: [], + severity: InvalidSyntax, + data: [], + }, +) +Diagnostic( + DiagnosticData { + message: "Undefined variable", + span: Some( + SourceSpan { + offset: SourceOffset( + 0, + ), + length: 1, + }, + ), + related_information: [], + severity: InvalidSyntax, + data: [], + }, +) +Diagnostic( + DiagnosticData { + message: "Undefined variable", + span: Some( + SourceSpan { + offset: SourceOffset( + 0, + ), + length: 1, + }, + ), + related_information: [], + severity: InvalidSyntax, + data: [], + }, +) +Diagnostic( + DiagnosticData { + message: "Undefined variable", + span: Some( + SourceSpan { + offset: SourceOffset( + 0, + ), + length: 1, + }, + ), + related_information: [], + severity: InvalidSyntax, + data: [], + }, +) +Diagnostic( + DiagnosticData { + message: "Undefined variable", + span: Some( + SourceSpan { + offset: SourceOffset( + 0, + ), + length: 1, + }, + ), + related_information: [], + severity: InvalidSyntax, + data: [], + }, +) +Diagnostic( + DiagnosticData { + message: "Undefined variable", + span: Some( + SourceSpan { + offset: SourceOffset( + 0, + ), + length: 1, + }, + ), + related_information: [], + severity: InvalidSyntax, + data: [], + }, +) +Diagnostic( + DiagnosticData { + message: "Undefined variable", + span: Some( + SourceSpan { + offset: SourceOffset( + 0, + ), + length: 1, + }, + ), + related_information: [], + severity: InvalidSyntax, + data: [], + }, +) + diff --git a/compiler/forget/crates/forget_semantic_analysis/tests/snapshots/analysis_test__fixtures.snap b/compiler/forget/crates/forget_semantic_analysis/tests/snapshots/analysis_test__fixtures@labels.js.snap similarity index 81% rename from compiler/forget/crates/forget_semantic_analysis/tests/snapshots/analysis_test__fixtures.snap rename to compiler/forget/crates/forget_semantic_analysis/tests/snapshots/analysis_test__fixtures@labels.js.snap index f84b04b40f..fb540061c3 100644 --- a/compiler/forget/crates/forget_semantic_analysis/tests/snapshots/analysis_test__fixtures.snap +++ b/compiler/forget/crates/forget_semantic_analysis/tests/snapshots/analysis_test__fixtures@labels.js.snap @@ -1,6 +1,6 @@ --- source: crates/forget_semantic_analysis/tests/analysis_test.rs -expression: "format!(\"Input:\\n{input}\\n\\nAST:\\n{ast_output}\\n\\nAnalysis:\\n{analysis_output}\")" +expression: "format!(\"Input:\\n{input}\\n\\nAST:\\n{ast_output}\\n\\nAnalysis:\\n{output}\")" input_file: crates/forget_semantic_analysis/tests/fixtures/labels.js --- Input: @@ -517,43 +517,8 @@ Scope { 2, ), }, - "x": Declaration { - id: DeclarationId( - 3, - ), - kind: Let, - scope: ScopeId( - 2, - ), - }, }, references: [ - Reference { - id: ReferenceId( - 0, - ), - kind: Read, - declaration: DeclarationId( - 3, - ), - declaration (name): "x", - scope: ScopeId( - 2, - ), - }, - Reference { - id: ReferenceId( - 1, - ), - kind: Read, - declaration: DeclarationId( - 3, - ), - declaration (name): "x", - scope: ScopeId( - 2, - ), - }, Reference { id: ReferenceId( 6, @@ -573,13 +538,23 @@ Scope { id: ScopeId( 3, ), - kind: Block, + kind: For, labels: {}, - declarations: {}, + declarations: { + "x": Declaration { + id: DeclarationId( + 3, + ), + kind: Let, + scope: ScopeId( + 3, + ), + }, + }, references: [ Reference { id: ReferenceId( - 2, + 0, ), kind: Read, declaration: DeclarationId( @@ -592,20 +567,7 @@ Scope { }, Reference { id: ReferenceId( - 3, - ), - kind: ReadWrite, - declaration: DeclarationId( - 2, - ), - declaration (name): "y", - scope: ScopeId( - 3, - ), - }, - Reference { - id: ReferenceId( - 4, + 1, ), kind: Read, declaration: DeclarationId( @@ -616,19 +578,6 @@ Scope { 3, ), }, - Reference { - id: ReferenceId( - 5, - ), - kind: Read, - declaration: DeclarationId( - 2, - ), - declaration (name): "y", - scope: ScopeId( - 3, - ), - }, ], children: [ Scope { @@ -638,14 +587,78 @@ Scope { kind: Block, labels: {}, declarations: {}, - references: [], - children: [], + references: [ + Reference { + id: ReferenceId( + 2, + ), + kind: Read, + declaration: DeclarationId( + 3, + ), + declaration (name): "x", + scope: ScopeId( + 4, + ), + }, + Reference { + id: ReferenceId( + 3, + ), + kind: ReadWrite, + declaration: DeclarationId( + 2, + ), + declaration (name): "y", + scope: ScopeId( + 4, + ), + }, + Reference { + id: ReferenceId( + 4, + ), + kind: Read, + declaration: DeclarationId( + 3, + ), + declaration (name): "x", + scope: ScopeId( + 4, + ), + }, + Reference { + id: ReferenceId( + 5, + ), + kind: Read, + declaration: DeclarationId( + 2, + ), + declaration (name): "y", + scope: ScopeId( + 4, + ), + }, + ], + children: [ + Scope { + id: ScopeId( + 5, + ), + kind: Block, + labels: {}, + declarations: {}, + references: [], + children: [], + }, + ], }, ], }, Scope { id: ScopeId( - 5, + 6, ), kind: Block, labels: {}, @@ -659,3 +672,4 @@ Scope { }, ], } +