@samitouri / QOS-React-1 / commits / 6bca9fb7a3

[rust] Update HIR builder to use new semantic analysis

Updates HIR builder to rely on the new semantic analysis instead of assuming that the ast nodes will already have binding info attached. That was a stopgap until we had our own name resolution :-) Once this lands we can remove SWC and switch everything to forget_hermes_parser, and also remove the non-spec Identifier.binding field (which stored the temporary name resolution data).

Joe Savona committed Aug 10, 2023 at 10:59 UTC 6bca9fb7a3f991b39c14a3f1c8a7bb00b1510a9a
25 files changed +874 -1573
compiler/forget/Cargo.lock
+3
@@ -462,6 +462,7 @@ dependencies = [
462 "forget_diagnostics",
463 "forget_estree",
464 "forget_hir",
465 + "forget_semantic_analysis",
466 "indexmap 2.0.0",
467 "thiserror",
468 ]
@@ -517,6 +518,7 @@ dependencies = [
518 "forget_estree_swc",
519 "forget_hir",
520 "forget_optimization",
521 + "forget_semantic_analysis",
522 "forget_ssa",
523 "insta",
524 "miette 5.9.0",
@@ -541,6 +543,7 @@ version = "0.1.0"
543 dependencies = [
544 "forget_diagnostics",
545 "forget_estree",
546 + "forget_semantic_analysis",
547 "forget_utils",
548 "indexmap 2.0.0",
549 "serde",
compiler/forget/crates/forget_build_hir/Cargo.toml
+1
@@ -17,4 +17,5 @@ forget_hir = { workspace = true }
17 forget_estree = { workspace = true}
18 indexmap = { workspace = true }
19 forget_diagnostics = { workspace = true }
20 +forget_semantic_analysis = { workspace = true }
21 thiserror = { workspace = true }
compiler/forget/crates/forget_build_hir/src/build.rs
+199 -198
@@ -3,8 +3,9 @@ use std::collections::HashSet;
3 use forget_diagnostics::Diagnostic;
4 use forget_estree::{
5 AssignmentTarget, BinaryExpression, BlockStatement, Expression, ExpressionOrSpread,
6 - ExpressionOrSuper, ForInit, ForStatement, Function, IfStatement, JsValue, Literal, Pattern,
7 - Statement, VariableDeclarationKind,
6 + ExpressionOrSuper, ForInit, ForStatement, Function, FunctionExpression, IfStatement,
7 + IntoFunction, JsValue, Literal, Pattern, Statement, VariableDeclaration,
8 + VariableDeclarationKind,
9 };
10 use forget_hir::{
11 BlockKind, BranchTerminal, Environment, ForTerminal, GotoKind, IdentifierOperand, InstrIx,
@@ -22,33 +23,18 @@ use crate::error::BuildHIRError;
23 ///
24 /// Failures generally include nonsensical input (`delete 1`) or syntax
25 /// that is not yet supported.
25 -pub fn build(env: &Environment, fun: Function) -> Result<Box<forget_hir::Function>, Diagnostic> {
26 +pub fn build(env: &Environment, fun: &Function) -> Result<Box<forget_hir::Function>, Diagnostic> {
27 let mut builder = Builder::new(env);
28
28 - match fun.body {
29 - Some(forget_estree::FunctionBody::BlockStatement(body)) => {
30 - lower_block_statement(env, &mut builder, *body)?
31 - }
32 - Some(forget_estree::FunctionBody::Expression(body)) => {
33 - lower_expression(env, &mut builder, body)?;
34 - }
35 - None => {
36 - return Err(Diagnostic::invalid_syntax(
37 - BuildHIRError::EmptyFunction,
38 - fun.range,
39 - ));
40 - }
41 - }
42 -
29 let mut params = Vec::with_capacity(fun.params.len());
44 - for param in fun.params {
30 + for param in &fun.params {
31 match param {
32 Pattern::Identifier(param) => {
33 let identifier = lower_identifier_for_assignment(
34 env,
35 &mut builder,
36 InstructionKind::Let,
51 - *param,
37 + param,
38 )?;
39 params.push(identifier);
40 }
@@ -61,6 +47,21 @@ pub fn build(env: &Environment, fun: Function) -> Result<Box<forget_hir::Functio
47 }
48 }
49
50 + match &fun.body {
51 + Some(forget_estree::FunctionBody::BlockStatement(body)) => {
52 + lower_block_statement(env, &mut builder, body)?
53 + }
54 + Some(forget_estree::FunctionBody::Expression(body)) => {
55 + lower_expression(env, &mut builder, body)?;
56 + }
57 + None => {
58 + return Err(Diagnostic::invalid_syntax(
59 + BuildHIRError::EmptyFunction,
60 + fun.range,
61 + ));
62 + }
63 + }
64 +
65 // In case the function did not explicitly return, terminate the final
66 // block with an explicit `return undefined`. If the function *did* return,
67 // this will be unreachable and get pruned later.
@@ -79,7 +80,7 @@ pub fn build(env: &Environment, fun: Function) -> Result<Box<forget_hir::Functio
80
81 let body = builder.build()?;
82 Ok(Box::new(forget_hir::Function {
82 - id: fun.id.map(|id| id.name),
83 + id: fun.id.as_ref().map(|id| id.name.clone()),
84 body,
85 params,
86 // TODO: populate context!
@@ -92,9 +93,9 @@ pub fn build(env: &Environment, fun: Function) -> Result<Box<forget_hir::Functio
93 fn lower_block_statement(
94 env: &Environment,
95 builder: &mut Builder,
95 - stmt: BlockStatement,
96 + stmt: &BlockStatement,
97 ) -> Result<(), Diagnostic> {
97 - for stmt in stmt.body {
98 + for stmt in &stmt.body {
99 lower_statement(env, builder, stmt, None)?;
100 }
101 Ok(())
@@ -105,12 +106,12 @@ fn lower_block_statement(
106 fn lower_statement(
107 env: &Environment,
108 builder: &mut Builder,
108 - stmt: Statement,
109 + stmt: &Statement,
110 label: Option<String>,
111 ) -> Result<(), Diagnostic> {
112 match stmt {
113 Statement::BlockStatement(stmt) => {
113 - lower_block_statement(env, builder, *stmt)?;
114 + lower_block_statement(env, builder, stmt)?;
115 }
116 Statement::BreakStatement(stmt) => {
117 let block = builder.resolve_break(stmt.label.as_ref())?;
@@ -133,7 +134,7 @@ fn lower_statement(
134 );
135 }
136 Statement::ReturnStatement(stmt) => {
136 - let ix = match stmt.argument {
137 + let ix = match &stmt.argument {
138 Some(argument) => lower_expression(env, builder, argument)?,
139 None => builder.push(InstructionValue::Primitive(forget_hir::Primitive {
140 value: PrimitiveValue::Undefined,
@@ -147,82 +148,21 @@ fn lower_statement(
148 );
149 }
150 Statement::ExpressionStatement(stmt) => {
150 - lower_expression(env, builder, stmt.expression)?;
151 + lower_expression(env, builder, &stmt.expression)?;
152 }
153 Statement::EmptyStatement(_) => {
154 // no-op
155 }
156 Statement::VariableDeclaration(stmt) => {
156 - let kind = match stmt.kind {
157 - VariableDeclarationKind::Const => InstructionKind::Const,
158 - VariableDeclarationKind::Let => InstructionKind::Let,
159 - VariableDeclarationKind::Var => {
160 - return Err(Diagnostic::unsupported(
161 - BuildHIRError::VariableDeclarationKindIsVar,
162 - stmt.range,
163 - ));
164 - }
165 - };
166 - for declaration in stmt.declarations {
167 - if let Some(init) = declaration.init {
168 - let value = lower_expression(env, builder, init)?;
169 - lower_assignment(
170 - env,
171 - builder,
172 - kind,
173 - AssignmentTarget::Pattern(declaration.id.into()),
174 - value,
175 - )?;
176 - } else {
177 - match declaration.id {
178 - Pattern::Identifier(id) => {
179 - // TODO: handle unbound variables
180 - let binding = builder.resolve_identifier(&id)?;
181 - let identifier = match binding {
182 - Binding::Local(identifier) => identifier,
183 - _ => {
184 - return Err(Diagnostic::invariant(
185 - BuildHIRError::VariableDeclarationBindingIsNonLocal,
186 - id.range,
187 - ));
188 - }
189 - };
190 - builder.push(InstructionValue::DeclareLocal(
191 - forget_hir::DeclareLocal {
192 - lvalue: LValue {
193 - identifier: IdentifierOperand {
194 - identifier,
195 - effect: None,
196 - },
197 - kind,
198 - },
199 - },
200 - ));
201 - }
202 - _ => {
203 - return Err(Diagnostic::todo(
204 - "Handle non-identifier variable declarations",
205 - declaration.range,
206 - ));
207 - }
208 - }
209 - }
210 - }
157 + lower_variable_declaration(env, builder, stmt)?;
158 }
159 Statement::IfStatement(stmt) => {
160 // block for what follows the if statement, though this may
161 // not be reachable
162 let fallthrough_block = builder.reserve(BlockKind::Block);
163
217 - let IfStatement {
218 - test,
219 - consequent,
220 - alternate,
221 - ..
222 - } = *stmt;
223 -
164 let consequent_block = builder.enter(BlockKind::Block, |builder| {
225 - lower_statement(env, builder, consequent, None)?;
165 + lower_statement(env, builder, &stmt.consequent, None)?;
166 Ok(TerminalValue::Goto(forget_hir::GotoTerminal {
167 block: fallthrough_block.id,
168 kind: GotoKind::Break,
@@ -230,7 +170,7 @@ fn lower_statement(
170 })?;
171
172 let alternate_block = builder.enter(BlockKind::Block, |builder| {
233 - if let Some(alternate) = alternate {
173 + if let Some(alternate) = &stmt.alternate {
174 lower_statement(env, builder, alternate, None)?;
175 }
176 Ok(TerminalValue::Goto(forget_hir::GotoTerminal {
@@ -239,7 +179,7 @@ fn lower_statement(
179 }))
180 })?;
181
242 - let test = lower_expression(env, builder, test)?;
182 + let test = lower_expression(env, builder, &stmt.test)?;
183 let terminal = TerminalValue::If(forget_hir::IfTerminal {
184 test: Operand {
185 ix: test,
@@ -252,14 +192,6 @@ fn lower_statement(
192 builder.terminate_with_fallthrough(terminal, fallthrough_block);
193 }
194 Statement::ForStatement(stmt) => {
255 - let ForStatement {
256 - init,
257 - test,
258 - update,
259 - body,
260 - ..
261 - } = *stmt;
262 -
195 // Block for the loop's test condition
196 let test_block = builder.reserve(BlockKind::Loop);
197
@@ -267,8 +199,8 @@ fn lower_statement(
199 let fallthrough_block = builder.reserve(BlockKind::Block);
200
201 let init_block = builder.enter(BlockKind::Loop, |builder| {
270 - if let Some(ForInit::VariableDeclaration(decl)) = init {
271 - lower_statement(env, builder, Statement::VariableDeclaration(decl), None)?;
202 + if let Some(ForInit::VariableDeclaration(decl)) = &stmt.init {
203 + lower_variable_declaration(env, builder, decl)?;
204 Ok(TerminalValue::Goto(forget_hir::GotoTerminal {
205 block: test_block.id,
206 kind: GotoKind::Break,
@@ -281,7 +213,9 @@ fn lower_statement(
213 }
214 })?;
215
284 - let update_block = update
216 + let update_block = stmt
217 + .update
218 + .as_ref()
219 .map(|update| {
220 builder.enter(BlockKind::Loop, |builder| {
221 lower_expression(env, builder, update)?;
@@ -300,7 +234,7 @@ fn lower_statement(
234 break_block: fallthrough_block.id,
235 };
236 builder.enter_loop(loop_, |builder| {
303 - lower_statement(env, builder, body, None)?;
237 + lower_statement(env, builder, &stmt.body, None)?;
238 Ok(TerminalValue::Goto(forget_hir::GotoTerminal {
239 block: update_block.unwrap_or(test_block.id),
240 kind: GotoKind::Continue,
@@ -317,7 +251,7 @@ fn lower_statement(
251 });
252 builder.terminate_with_fallthrough(terminal, test_block);
253
320 - if let Some(test) = test {
254 + if let Some(test) = &stmt.test {
255 let test_value = lower_expression(env, builder, test)?;
256 let terminal = TerminalValue::Branch(BranchTerminal {
257 test: Operand {
@@ -340,6 +274,58 @@ fn lower_statement(
274 Ok(())
275 }
276
277 +fn lower_variable_declaration(
278 + env: &Environment,
279 + builder: &mut Builder,
280 + stmt: &VariableDeclaration,
281 +) -> Result<(), Diagnostic> {
282 + let kind = match stmt.kind {
283 + VariableDeclarationKind::Const => InstructionKind::Const,
284 + VariableDeclarationKind::Let => InstructionKind::Let,
285 + VariableDeclarationKind::Var => {
286 + return Err(Diagnostic::unsupported(
287 + BuildHIRError::VariableDeclarationKindIsVar,
288 + stmt.range,
289 + ));
290 + }
291 + };
292 + for declaration in &stmt.declarations {
293 + if let Some(init) = &declaration.init {
294 + let value = lower_expression(env, builder, init)?;
295 + lower_assignment_pattern(env, builder, kind, &declaration.id, value)?;
296 + } else {
297 + match &declaration.id {
298 + Pattern::Identifier(id) => {
299 + let identifier = env.resolve_variable_declaration(id.as_ref(), &id.name);
300 + if let Some(identifier) = identifier {
301 + builder.push(InstructionValue::DeclareLocal(forget_hir::DeclareLocal {
302 + lvalue: LValue {
303 + identifier: IdentifierOperand {
304 + identifier,
305 + effect: None,
306 + },
307 + kind,
308 + },
309 + }));
310 + } else {
311 + return Err(Diagnostic::invariant(
312 + BuildHIRError::VariableDeclarationBindingIsNonLocal,
313 + id.range,
314 + ));
315 + }
316 + }
317 + _ => {
318 + return Err(Diagnostic::todo(
319 + "Handle non-identifier variable declarations",
320 + declaration.range,
321 + ));
322 + }
323 + }
324 + }
325 + }
326 + Ok(())
327 +}
328 +
329 /// Converts an ESTree Expression into an HIR InstructionValue. Note that while only a single
330 /// InstructionValue is returned, this function is recursive and may cause multiple instructions
331 /// to be emitted, possibly across multiple basic blocks (in the case of expressions with control
@@ -347,35 +333,33 @@ fn lower_statement(
333 fn lower_expression(
334 env: &Environment,
335 builder: &mut Builder,
350 - expr: Expression,
336 + expr: &Expression,
337 ) -> Result<InstrIx, Diagnostic> {
338 let value = match expr {
339 Expression::Identifier(expr) => {
354 - // TODO: handle unbound variables
355 - let binding = builder.resolve_identifier(&expr)?;
356 - match binding {
357 - Binding::Local(identifier) => {
358 - let place = IdentifierOperand {
359 - effect: None,
360 - identifier,
361 - };
362 - InstructionValue::LoadLocal(LoadLocal { place })
363 - }
364 - Binding::Module(..) | Binding::Global => {
365 - InstructionValue::LoadGlobal(LoadGlobal { name: expr.name })
366 - }
340 + let identifier = env.resolve_variable_reference(expr.as_ref());
341 + if let Some(identifier) = identifier {
342 + let place = IdentifierOperand {
343 + effect: None,
344 + identifier,
345 + };
346 + InstructionValue::LoadLocal(LoadLocal { place })
347 + } else {
348 + InstructionValue::LoadGlobal(LoadGlobal {
349 + name: expr.name.clone(),
350 + })
351 }
352 }
353 Expression::Literal(expr) => InstructionValue::Primitive(forget_hir::Primitive {
370 - value: lower_primitive(env, builder, *expr),
354 + value: lower_primitive(env, builder, expr),
355 }),
356 Expression::ArrayExpression(expr) => {
357 let mut elements = Vec::with_capacity(expr.elements.len());
374 - for expr in expr.elements {
358 + for expr in &expr.elements {
359 let element = match expr {
360 Some(forget_estree::ExpressionOrSpread::SpreadElement(expr)) => {
361 Some(PlaceOrSpread::Spread(Operand {
378 - ix: lower_expression(env, builder, expr.argument)?,
362 + ix: lower_expression(env, builder, &expr.argument)?,
363 effect: None,
364 }))
365 }
@@ -394,12 +378,12 @@ fn lower_expression(
378
379 Expression::AssignmentExpression(expr) => match expr.operator {
380 forget_estree::AssignmentOperator::Equals => {
397 - let right = lower_expression(env, builder, expr.right)?;
381 + let right = lower_expression(env, builder, &expr.right)?;
382 return Ok(lower_assignment(
383 env,
384 builder,
385 InstructionKind::Reassign,
402 - expr.left,
386 + &expr.left,
387 right,
388 )?);
389 }
@@ -407,20 +391,14 @@ fn lower_expression(
391 },
392
393 Expression::BinaryExpression(expr) => {
410 - let BinaryExpression {
411 - left,
412 - operator,
413 - right,
414 - ..
415 - } = *expr;
416 - let left = lower_expression(env, builder, left)?;
417 - let right = lower_expression(env, builder, right)?;
394 + let left = lower_expression(env, builder, &expr.left)?;
395 + let right = lower_expression(env, builder, &expr.right)?;
396 InstructionValue::Binary(forget_hir::Binary {
397 left: Operand {
398 ix: left,
399 effect: None,
400 },
423 - operator,
401 + operator: expr.operator,
402 right: Operand {
403 ix: right,
404 effect: None,
@@ -429,15 +407,15 @@ fn lower_expression(
407 }
408
409 Expression::FunctionExpression(expr) => {
432 - InstructionValue::Function(lower_function(env, builder, expr.function)?)
410 + InstructionValue::Function(lower_function(env, builder, expr.as_ref())?)
411 }
412
413 Expression::ArrowFunctionExpression(expr) => {
436 - InstructionValue::Function(lower_function(env, builder, expr.function)?)
414 + InstructionValue::Function(lower_function(env, builder, expr.as_ref())?)
415 }
416
417 Expression::CallExpression(expr) => {
440 - let callee_expr = match expr.callee {
418 + let callee_expr = match &expr.callee {
419 ExpressionOrSuper::Super(callee) => {
420 return Err(Diagnostic::unsupported(
421 BuildHIRError::UnsupportedSuperExpression,
@@ -451,8 +429,8 @@ fn lower_expression(
429 return Err(Diagnostic::todo("Support method calls", expr.range));
430 }
431
454 - let callee = lower_expression(env, builder, callee_expr)?;
455 - let arguments = lower_arguments(env, builder, expr.arguments)?;
432 + let callee = lower_expression(env, builder, &callee_expr)?;
433 + let arguments = lower_arguments(env, builder, &expr.arguments)?;
434 InstructionValue::Call(forget_hir::Call {
435 callee: Operand {
436 ix: callee,
@@ -463,7 +441,7 @@ fn lower_expression(
441 }
442
443 Expression::JSXElement(expr) => {
466 - InstructionValue::JSXElement(lower_jsx_element(env, builder, *expr)?)
444 + InstructionValue::JSXElement(lower_jsx_element(env, builder, expr)?)
445 }
446
447 _ => todo!("Lower expr {expr:#?}"),
@@ -474,14 +452,14 @@ fn lower_expression(
452 fn lower_arguments(
453 env: &Environment,
454 builder: &mut Builder,
477 - args: Vec<ExpressionOrSpread>,
455 + args: &[ExpressionOrSpread],
456 ) -> Result<Vec<PlaceOrSpread>, Diagnostic> {
457 let mut arguments = Vec::with_capacity(args.len());
458 for arg in args {
459 let element = match arg {
460 forget_estree::ExpressionOrSpread::SpreadElement(arg) => {
461 PlaceOrSpread::Spread(Operand {
484 - ix: lower_expression(env, builder, arg.argument)?,
462 + ix: lower_expression(env, builder, &arg.argument)?,
463 effect: None,
464 })
465 }
@@ -495,31 +473,28 @@ fn lower_arguments(
473 Ok(arguments)
474 }
475
498 -fn lower_function(
476 +fn lower_function<T: IntoFunction>(
477 env: &Environment,
500 - builder: &mut Builder,
501 - function: forget_estree::Function,
478 + _builder: &mut Builder,
479 + function: &T,
480 ) -> Result<forget_hir::FunctionExpression, Diagnostic> {
481 println!("get_context_identifiers() ...");
504 - let context_identifiers = get_context_identifiers(env, &function);
482 + let context_identifiers = get_context_identifiers(env, function);
483 println!("ok");
484 let mut context = Vec::new();
485 let mut seen = HashSet::new();
508 - for identifier in context_identifiers {
509 - match builder.resolve_identifier(identifier)? {
510 - Binding::Local(identifier) => {
511 - if !seen.insert(identifier.id) {
512 - continue;
513 - }
514 - context.push(IdentifierOperand {
515 - effect: None,
516 - identifier,
517 - });
486 + for declaration_id in context_identifiers {
487 + if let Some(identifier) = env.resolve_declaration_id(declaration_id) {
488 + if !seen.insert(identifier.id) {
489 + continue;
490 }
519 - _ => {}
491 + context.push(IdentifierOperand {
492 + effect: None,
493 + identifier,
494 + });
495 }
496 }
522 - let mut fun = build(env, function)?;
497 + let mut fun = build(env, function.function())?;
498 fun.context = context;
499 Ok(forget_hir::FunctionExpression {
500 // TODO: collect dependencies!
@@ -531,18 +506,18 @@ fn lower_function(
506 fn lower_jsx_element(
507 env: &Environment,
508 builder: &mut Builder,
534 - expr: forget_estree::JSXElement,
509 + expr: &forget_estree::JSXElement,
510 ) -> Result<JSXElement, Diagnostic> {
511 let props: Result<Vec<JSXAttribute>, Diagnostic> = expr
512 .opening_element
513 .attributes
539 - .into_iter()
514 + .iter()
515 .map(|attr| lower_jsx_attribute(env, builder, attr))
516 .collect();
517 let props = props?;
518 let children: Result<Vec<Operand>, Diagnostic> = expr
519 .children
545 - .into_iter()
520 + .iter()
521 .map(|child| {
522 let ix = lower_jsx_child(env, builder, child)?;
523 Ok(Operand { effect: None, ix })
@@ -564,7 +539,7 @@ fn lower_jsx_element(
539 fn lower_jsx_attribute(
540 env: &Environment,
541 builder: &mut Builder,
567 - attr: forget_estree::JSXAttributeOrSpread,
542 + attr: &forget_estree::JSXAttributeOrSpread,
543 ) -> Result<JSXAttribute, Diagnostic> {
544 todo!("lower jsx attribute")
545 }
@@ -572,7 +547,7 @@ fn lower_jsx_attribute(
547 fn lower_jsx_child(
548 env: &Environment,
549 builder: &mut Builder,
575 - child: forget_estree::JSXChildItem,
550 + child: &forget_estree::JSXChildItem,
551 ) -> Result<InstrIx, Diagnostic> {
552 todo!("lower jsx child")
553 }
@@ -581,57 +556,83 @@ fn lower_assignment(
556 env: &Environment,
557 builder: &mut Builder,
558 kind: InstructionKind,
584 - lvalue: AssignmentTarget,
559 + lvalue: &AssignmentTarget,
560 value: InstrIx,
561 ) -> Result<InstrIx, Diagnostic> {
562 Ok(match lvalue {
588 - AssignmentTarget::Pattern(lvalue) => match lvalue {
589 - Pattern::Identifier(lvalue) => {
590 - let identifier = lower_identifier_for_assignment(env, builder, kind, *lvalue)?;
591 - builder.push(InstructionValue::StoreLocal(forget_hir::StoreLocal {
592 - lvalue: LValue { identifier, kind },
593 - value: Operand {
594 - ix: value,
595 - effect: None,
596 - },
597 - }))
598 - }
599 - _ => todo!("lower assignment pattern for {:#?}", lvalue),
600 - },
563 + AssignmentTarget::Pattern(lvalue) => {
564 + lower_assignment_pattern(env, builder, kind, lvalue, value)?
565 + }
566 _ => todo!("lower assignment for {:#?}", lvalue),
567 })
568 }
569
570 +fn lower_assignment_pattern(
571 + env: &Environment,
572 + builder: &mut Builder,
573 + kind: InstructionKind,
574 + lvalue: &Pattern,
575 + value: InstrIx,
576 +) -> Result<InstrIx, Diagnostic> {
577 + Ok(match lvalue {
578 + Pattern::Identifier(lvalue) => {
579 + let identifier = lower_identifier_for_assignment(env, builder, kind, lvalue)?;
580 + builder.push(InstructionValue::StoreLocal(forget_hir::StoreLocal {
581 + lvalue: LValue { identifier, kind },
582 + value: Operand {
583 + ix: value,
584 + effect: None,
585 + },
586 + }))
587 + }
588 + _ => todo!("lower assignment pattern for {:#?}", lvalue),
589 + })
590 +}
591 +
592 fn lower_identifier_for_assignment(
606 - _env: &Environment,
593 + env: &Environment,
594 builder: &mut Builder,
608 - _kind: InstructionKind,
609 - identifier: forget_estree::Identifier,
595 + kind: InstructionKind,
596 + node: &forget_estree::Identifier,
597 ) -> Result<IdentifierOperand, Diagnostic> {
611 - let binding = builder.resolve_identifier(&identifier)?;
612 - match binding {
613 - Binding::Module(..) | Binding::Global => Err(Diagnostic::invalid_react(
614 - BuildHIRError::ReassignedGlobal,
615 - identifier.range,
616 - )
617 - .annotate(
618 - format!("Cannot reassign `{}`", &identifier.name),
619 - identifier.range,
620 - )),
621 - Binding::Local(id) => Ok(IdentifierOperand {
622 - identifier: id,
623 - effect: None,
624 - }),
598 + match kind {
599 + InstructionKind::Reassign => {
600 + let identifier = env.resolve_variable_reference(node);
601 + if let Some(identifier) = identifier {
602 + Ok(IdentifierOperand {
603 + identifier,
604 + effect: None,
605 + })
606 + } else {
607 + // Reassigning a global
608 + Err(
609 + Diagnostic::invalid_react(BuildHIRError::ReassignedGlobal, node.range)
610 + .annotate(format!("Cannot reassign `{}`", &node.name), node.range),
611 + )
612 + }
613 + }
614 + _ => {
615 + // Declaration
616 + let identifier = env.resolve_variable_declaration(node, &node.name).unwrap();
617 + Ok(IdentifierOperand {
618 + identifier,
619 + effect: None,
620 + })
621 + }
622 }
623 }
624
625 /// Converts an ESTree literal into a HIR primitive
629 -fn lower_primitive(_env: &Environment, _builder: &mut Builder, literal: Literal) -> PrimitiveValue {
630 - match literal.value {
631 - JsValue::Bool(bool) => PrimitiveValue::Boolean(bool),
626 +fn lower_primitive(
627 + _env: &Environment,
628 + _builder: &mut Builder,
629 + literal: &Literal,
630 +) -> PrimitiveValue {
631 + match &literal.value {
632 + JsValue::Bool(bool) => PrimitiveValue::Boolean(*bool),
633 JsValue::Null => PrimitiveValue::Null,
633 - JsValue::Number(value) => PrimitiveValue::Number(f64::from(value).into()),
634 - JsValue::String(s) => PrimitiveValue::String(s),
634 + JsValue::Number(value) => PrimitiveValue::Number(f64::from(*value).into()),
635 + JsValue::String(s) => PrimitiveValue::String(s.clone()),
636 _ => todo!("Lower literal {literal:#?}"),
637 }
638 }
compiler/forget/crates/forget_build_hir/src/builder.rs
-23
@@ -313,27 +313,4 @@ impl<'e> Builder<'e> {
313 None,
314 ))
315 }
316 -
317 - pub(crate) fn resolve_identifier(
318 - &mut self,
319 - identifier: &forget_estree::Identifier,
320 - ) -> Result<Binding, Diagnostic> {
321 - match &identifier.binding {
322 - Some(binding) => Ok(match binding {
323 - forget_estree::Binding::Global => Binding::Global,
324 - forget_estree::Binding::Local(id) => Binding::Local(
325 - self.environment
326 - .resolve_binding_identifier(&identifier.name, *id),
327 - ),
328 - forget_estree::Binding::Module(id) => Binding::Module(
329 - self.environment
330 - .resolve_binding_identifier(&identifier.name, *id),
331 - ),
332 - }),
333 - _ => Err(Diagnostic::invariant(
334 - BuildHIRError::UnknownIdentifier,
335 - identifier.range.clone(),
336 - )),
337 - }
338 - }
316 }
compiler/forget/crates/forget_build_hir/src/context.rs
+46 -60
@@ -1,72 +1,58 @@
1 use std::collections::HashSet;
2
3 -use forget_estree::{Binding, BindingId, Function, Identifier, Visitor_DEPRECATED};
3 +use forget_estree::IntoFunction;
4 use forget_hir::Environment;
5 +use forget_semantic_analysis::{DeclarationId, ScopeView};
6
6 -pub(crate) fn get_context_identifiers<'ast>(
7 - _env: &Environment,
8 - function: &'ast Function,
9 -) -> Vec<&'ast Identifier> {
10 - let mut visitor = ContextVisitor::new();
11 - visitor.visit_function(function);
12 - let ContextVisitor {
13 - free_variables,
14 - defined,
15 - ..
16 - } = visitor;
17 - free_variables
18 - .into_iter()
19 - .filter(|identifier| match &identifier.binding {
20 - Some(Binding::Local(id)) if !defined.contains(id) => true,
21 - _ => false,
22 - })
23 - .collect()
7 +pub(crate) fn get_context_identifiers<T: IntoFunction>(
8 + env: &Environment,
9 + node: &T,
10 +) -> Vec<DeclarationId> {
11 + let function_scope = env.scope(node.function()).unwrap();
12 + println!(
13 + "get_context_identifiers for function scope {:?}",
14 + function_scope.id()
15 + );
16 + let mut free = FreeVariables::default();
17 + let mut seen = HashSet::new();
18 + populate_free_variable_references(&mut free, &mut seen, function_scope);
19 + free
20 }
21
26 -struct ContextVisitor<'ast> {
27 - free_variables: Vec<&'ast Identifier>,
28 - defined: HashSet<BindingId>,
29 - lvalue: bool,
30 -}
22 +type FreeVariables = Vec<DeclarationId>;
23
32 -impl<'ast> ContextVisitor<'ast> {
33 - fn new() -> Self {
34 - Self {
35 - free_variables: Default::default(),
36 - defined: Default::default(),
37 - lvalue: false,
24 +fn populate_free_variable_references(
25 + free: &mut FreeVariables,
26 + seen: &mut HashSet<DeclarationId>,
27 + scope: ScopeView<'_>,
28 +) {
29 + for reference in scope.references() {
30 + if !seen.insert(reference.declaration().id()) {
31 + println!(
32 + "skip {}${:?}",
33 + reference.declaration().name(),
34 + reference.declaration().id()
35 + );
36 + continue;
37 }
39 - }
40 -}
41 -
42 -impl<'ast> Visitor_DEPRECATED<'ast> for ContextVisitor<'ast> {
43 - fn visit_lvalue<F>(&mut self, f: F)
44 - where
45 - F: FnOnce(&mut Self) -> (),
46 - {
47 - let prev_lvalue = self.lvalue;
48 - self.lvalue = true;
49 - f(self);
50 - self.lvalue = prev_lvalue;
51 - }
52 -
53 - fn visit_identifier(&mut self, identifier: &'ast Identifier) {
54 - let binding = identifier.binding.unwrap();
55 - match binding {
56 - Binding::Local(binding_id) => {
57 - if self.lvalue {
58 - // println!("lvalue {identifier:?}");
59 - self.defined.insert(binding_id);
60 - } else {
61 - // println!("rvalue {identifier:?}");
62 - self.free_variables.push(identifier);
63 - }
64 - }
65 - _ => {}
38 + let declaration_scope = reference.declaration().scope();
39 + if !declaration_scope.is_descendant_of(scope) {
40 + println!(
41 + "free variable: not descendant {}${:?}",
42 + reference.declaration().name(),
43 + reference.declaration().id()
44 + );
45 + free.push(reference.declaration().id())
46 + } else {
47 + println!(
48 + "local variable: descendant {}${:?} scope={:?}",
49 + reference.declaration().name(),
50 + reference.declaration().id(),
51 + reference.declaration().scope().id()
52 + );
53 }
54 }
68 -
69 - fn visit_literal(&mut self, _literal: &'ast forget_estree::Literal) {
70 - // no-op
55 + for child in scope.children() {
56 + populate_free_variable_references(free, seen, child);
57 }
58 }
compiler/forget/crates/forget_estree/src/generated_extensions.rs
+50 -2
@@ -1,7 +1,7 @@
1 // Manual extensions to generated types
2 use crate::{
3 - JSXElementName, JSXMemberExpression, JSXMemberExpressionOrIdentifier, Pattern, SourceRange,
4 - SourceType,
3 + ArrowFunctionExpression, Function, FunctionDeclaration, FunctionExpression, JSXElementName,
4 + JSXMemberExpression, JSXMemberExpressionOrIdentifier, Pattern, SourceRange, SourceType,
5 };
6
7 /// Sentinel trait to distinguish AST *node* types
@@ -43,3 +43,51 @@ impl JSXMemberExpression {
43 }
44 }
45 }
46 +
47 +pub trait IntoFunction: ESTreeNode {
48 + fn function(&self) -> &Function;
49 +
50 + fn into_function(self) -> Function;
51 +}
52 +
53 +impl IntoFunction for FunctionDeclaration {
54 + fn function(&self) -> &Function {
55 + &self.function
56 + }
57 +
58 + fn into_function(self) -> Function {
59 + self.function
60 + }
61 +}
62 +
63 +impl IntoFunction for FunctionExpression {
64 + fn function(&self) -> &Function {
65 + &self.function
66 + }
67 +
68 + fn into_function(self) -> Function {
69 + self.function
70 + }
71 +}
72 +
73 +impl IntoFunction for ArrowFunctionExpression {
74 + fn function(&self) -> &Function {
75 + &self.function
76 + }
77 +
78 + fn into_function(self) -> Function {
79 + self.function
80 + }
81 +}
82 +
83 +impl ESTreeNode for Function {}
84 +
85 +impl IntoFunction for Function {
86 + fn function(&self) -> &Function {
87 + self
88 + }
89 +
90 + fn into_function(self) -> Function {
91 + self
92 + }
93 +}
compiler/forget/crates/forget_estree/src/lib.rs
+1 -1
@@ -7,7 +7,7 @@ mod visit;
7
8 pub use binding::{Binding, BindingId};
9 pub use generated::*;
10 -pub use generated_extensions::ESTreeNode;
10 +pub use generated_extensions::*;
11 pub use js_value::{JsValue, Number};
12 pub use range::SourceRange;
13 pub use visit::*;
compiler/forget/crates/forget_fixtures/Cargo.toml
+1
@@ -18,6 +18,7 @@ forget_estree = { workspace = true }
18 forget_estree_swc = { workspace = true }
19 forget_hir = { workspace = true }
20 forget_optimization = { workspace = true }
21 +forget_semantic_analysis = { workspace = true }
22 forget_ssa = { workspace = true }
23 forget_build_hir = { workspace = true }
24 miette = { workspace = true, features = ["backtrace", "fancy"] }
compiler/forget/crates/forget_fixtures/tests/fixtures/simple-function.js new
+9
@@ -0,0 +1,9 @@
1 +function Component(a) {
2 + Math;
3 + let b = 0;
4 + const foo = function foo_(c) {
5 + let d = 1;
6 + return a + b + c + d;
7 + };
8 + return foo();
9 +}
compiler/forget/crates/forget_fixtures/tests/fixtures_test.rs
+12 -8
@@ -6,6 +6,7 @@ use forget_estree::{ModuleItem, Statement};
6 use forget_estree_swc::parse;
7 use forget_hir::{inline_use_memo, Environment, Features, Print, Registry};
8 use forget_optimization::constant_propagation;
9 +use forget_semantic_analysis::analyze;
10 use forget_ssa::{eliminate_redundant_phis, enter_ssa};
11 use insta::{assert_snapshot, glob};
12 use miette::{NamedSource, Report};
@@ -20,19 +21,22 @@ fn fixtures() {
21
22 let mut output = String::new();
23
23 - for (ix, item) in ast.body.into_iter().enumerate() {
24 + let analysis = analyze(&ast);
25 + // println!("{:#?}", analysis.debug());
26 + let environment = Environment::new(
27 + Features {
28 + validate_frozen_lambdas: true,
29 + },
30 + Registry,
31 + analysis,
32 + );
33 + for (ix, item) in ast.body.iter().enumerate() {
34 if let ModuleItem::Statement(stmt) = item {
35 if let Statement::FunctionDeclaration(fun) = stmt {
26 - let environment = Environment::new(
27 - Features {
28 - validate_frozen_lambdas: true,
29 - },
30 - Registry,
31 - );
36 if ix != 0 {
37 output.push_str("\n\n");
38 }
35 - match build(&environment, fun.function) {
39 + match build(&environment, &fun.function) {
40 Ok(mut fun) => {
41 // println!("{fun:#?}");
42 // let mut out = String::new();
compiler/forget/crates/forget_fixtures/tests/snapshots/fixtures_test__fixtures@function-expressions.js.snap
+22 -22
@@ -29,54 +29,54 @@ function Component(props) {
29
30 Output:
31 function Component(
32 - unknown props$11,
32 + unknown props$10,
33 )
34 entry bb0
35 bb0 (block)
36 [0] #0 = 2
37 - [1] #1 = StoreLocal Const unknown x$12 = unknown #0
38 - [2] #2 = Function @deps[] @context[unknown x$12, unknown foo$1]:
37 + [1] #1 = StoreLocal Const unknown x$11 = unknown #0
38 + [2] #2 = Function @deps[] @context[unknown x$11]:
39 function foo(
40 - unknown y$13,
40 + unknown y$12,
41 )
42 entry bb1
43 bb1 (block)
44 [0] #0 = 1
45 - [1] #1 = StoreLocal Let unknown a$14 = unknown #0
46 - [2] #2 = DeclareLocal Let unknown b$15
45 + [1] #1 = StoreLocal Let unknown a$13 = unknown #0
46 + [2] #2 = DeclareLocal Let unknown b$14
47 [3] #9 = 1
48 [4] #10 = 1
49 [5] #11 = true
50 [6] #3 = 5
51 [7] #4 = 3
52 [8] #5 = 8
53 - [9] #6 = StoreLocal Reassign unknown b$16 = unknown #5
53 + [9] #6 = StoreLocal Reassign unknown b$15 = unknown #5
54 [10] #12 = 2
55 - [11] #13 = LoadLocal unknown y$13
55 + [11] #13 = LoadLocal unknown y$12
56 [12] #14 = Binary unknown #12 + unknown #13
57 [13] #15 = 1
58 [14] #16 = Binary unknown #14 + unknown #15
59 [15] #17 = 8
60 [16] #18 = Binary unknown #16 + unknown #17
61 - [17] #19 = Function @deps[] @context[unknown x$18, unknown y$19, unknown a$20, unknown b$21]:
61 + [17] #19 = Function @deps[] @context[unknown x$17, unknown y$18, unknown a$19, unknown b$20]:
62 function bar(
63 - unknown z$22,
63 + unknown z$21,
64 )
65 entry bb5
66 bb5 (block)
67 [0] #0 = 2
68 - [1] #1 = StoreLocal Let unknown c$23 = unknown #0
69 - [2] #2 = DeclareLocal Let unknown d$24
68 + [1] #1 = StoreLocal Let unknown c$22 = unknown #0
69 + [2] #2 = DeclareLocal Let unknown d$23
70 [3] #3 = 3
71 - [4] #4 = StoreLocal Reassign unknown d$25 = unknown #3
72 - [5] #5 = LoadLocal unknown x$18
73 - [6] #6 = LoadLocal unknown y$19
71 + [4] #4 = StoreLocal Reassign unknown d$24 = unknown #3
72 + [5] #5 = LoadLocal unknown x$17
73 + [6] #6 = LoadLocal unknown y$18
74 [7] #7 = Binary unknown #5 + unknown #6
75 - [8] #8 = LoadLocal unknown a$20
75 + [8] #8 = LoadLocal unknown a$19
76 [9] #9 = Binary unknown #7 + unknown #8
77 - [10] #10 = LoadLocal unknown b$21
77 + [10] #10 = LoadLocal unknown b$20
78 [11] #11 = Binary unknown #9 + unknown #10
79 - [12] #12 = LoadLocal unknown z$22
79 + [12] #12 = LoadLocal unknown z$21
80 [13] #13 = Binary unknown #11 + unknown #12
81 [14] #14 = 2
82 [15] #15 = Binary unknown #13 + unknown #14
@@ -84,11 +84,11 @@ bb0 (block)
84 [17] #17 = Binary unknown #15 + unknown #16
85 [18] #18 = <undefined>
86 [19] Return unknown #18
87 - [18] #20 = StoreLocal Const unknown bar$26 = unknown #19
88 - [19] #21 = LoadLocal unknown bar$26
89 - [20] #22 = LoadLocal unknown foo$1
87 + [18] #20 = StoreLocal Const unknown bar$25 = unknown #19
88 + [19] #21 = LoadLocal unknown bar$25
89 + [20] #22 = LoadGlobal foo
90 [21] #23 = <undefined>
91 [22] Return unknown #23
92 - [3] #3 = StoreLocal Const unknown foo$28 = unknown #2
92 + [3] #3 = StoreLocal Const unknown foo$26 = unknown #2
93 [4] #4 = <undefined>
94 [5] Return unknown #4
compiler/forget/crates/forget_fixtures/tests/snapshots/fixtures_test__fixtures@identifiers.js.snap
+6 -6
@@ -24,18 +24,18 @@ function id(x) {
24
25 Output:
26 function id(
27 - unknown x$4,
27 + unknown x$3,
28 )
29 entry bb0
30 bb0 (block)
31 [0] #0 = LoadGlobal Math
32 [1] #1 = LoadGlobal id
33 [2] #2 = true
34 - [3] #3 = StoreLocal Let unknown y$5 = unknown #2
34 + [3] #3 = StoreLocal Let unknown y$4 = unknown #2
35 [4] #4 = false
36 - [5] #5 = StoreLocal Reassign unknown y$6 = unknown #4
36 + [5] #5 = StoreLocal Reassign unknown y$5 = unknown #4
37 [6] #6 = false
38 - [7] #7 = DeclareLocal Let unknown z$7
39 - [8] #8 = LoadLocal unknown z$7
40 - [9] #9 = LoadLocal unknown x$4
38 + [7] #7 = DeclareLocal Let unknown z$6
39 + [8] #8 = LoadLocal unknown z$6
40 + [9] #9 = LoadLocal unknown x$3
41 [10] Return unknown #9
compiler/forget/crates/forget_fixtures/tests/snapshots/fixtures_test__fixtures@simple-function.js.snap new
+46
@@ -0,0 +1,46 @@
1 +---
2 +source: crates/forget_fixtures/tests/fixtures_test.rs
3 +expression: "format!(\"Input:\\n{input}\\n\\nOutput:\\n{output}\")"
4 +input_file: crates/forget_fixtures/tests/fixtures/simple-function.js
5 +---
6 +Input:
7 +function Component(a) {
8 + Math;
9 + let b = 0;
10 + const foo = function foo_(c) {
11 + let d = 1;
12 + return a + b + c + d;
13 + };
14 + return foo();
15 +}
16 +
17 +
18 +Output:
19 +function Component(
20 + unknown a$5,
21 +)
22 +entry bb0
23 +bb0 (block)
24 + [0] #0 = LoadGlobal Math
25 + [1] #1 = 0
26 + [2] #2 = StoreLocal Let unknown b$6 = unknown #1
27 + [3] #3 = Function @deps[] @context[unknown a$5, unknown b$6]:
28 + function foo_(
29 + unknown c$7,
30 + )
31 + entry bb1
32 + bb1 (block)
33 + [0] #0 = 1
34 + [1] #1 = StoreLocal Let unknown d$8 = unknown #0
35 + [2] #2 = LoadLocal unknown a$5
36 + [3] #3 = 0
37 + [4] #4 = Binary unknown #2 + unknown #3
38 + [5] #5 = LoadLocal unknown c$7
39 + [6] #6 = Binary unknown #4 + unknown #5
40 + [7] #7 = 1
41 + [8] #8 = Binary unknown #6 + unknown #7
42 + [9] Return unknown #8
43 + [4] #4 = StoreLocal Const unknown foo$9 = unknown #3
44 + [5] #5 = LoadLocal unknown foo$9
45 + [6] #6 = Call unknown #5()
46 + [7] Return unknown #6
compiler/forget/crates/forget_fixtures/tests/snapshots/fixtures_test__fixtures@use-memo.js.snap
+6 -6
@@ -14,21 +14,21 @@ function Component(x) {
14
15 Output:
16 function Component(
17 - unknown x$1,
17 + unknown x$2,
18 )
19 entry bb0
20 bb0 (block)
21 [0] #0 = LoadGlobal useMemo
22 - [1] #6 = DeclareLocal Let unknown t$3
22 + [1] #6 = DeclareLocal Let unknown t$4
23 [2] Label block=bb1 fallthrough=bb6
24 bb1 (block)
25 predecessors: bb0
26 [3] #7 = LoadGlobal y
27 - [4] #9 = StoreLocal Reassign unknown t$3 = unknown #7
27 + [4] #9 = StoreLocal Reassign unknown t$4 = unknown #7
28 [5] Goto bb6
29 bb6 (block)
30 predecessors: bb1
31 - [6] #2 = LoadLocal unknown t$3
32 - [7] #3 = StoreLocal Const unknown x$2 = unknown #2
33 - [8] #4 = LoadLocal unknown x$2
31 + [6] #2 = LoadLocal unknown t$4
32 + [7] #3 = StoreLocal Const unknown x$3 = unknown #2
33 + [8] #4 = LoadLocal unknown x$3
34 [9] Return unknown #4
compiler/forget/crates/forget_hir/Cargo.toml
+1
@@ -18,4 +18,5 @@ indexmap = { workspace = true }
18 serde = { workspace = true }
19 forget_utils = { workspace = true }
20 forget_diagnostics = { workspace = true }
21 +forget_semantic_analysis = { workspace = true }
22 thiserror = { workspace = true }
compiler/forget/crates/forget_hir/src/environment.rs
+37 -10
@@ -1,8 +1,10 @@
1 use std::cell::{Cell, RefCell};
2 use std::collections::HashMap;
3 +use std::process::id;
4 use std::rc::Rc;
5
5 -use forget_estree::BindingId;
6 +use forget_estree::{BindingId, ESTreeNode};
7 +use forget_semantic_analysis::{AstNode, DeclarationId, ScopeManager, ScopeView};
8
9 use crate::{
10 BlockId, Features, Identifier, IdentifierData, IdentifierId, Registry, Type, TypeVarId,
@@ -27,16 +29,19 @@ pub struct Environment {
29 /// The next available identifier id
30 next_identifier_id: Cell<IdentifierId>,
31
32 + analysis: ScopeManager,
33 +
34 next_type_var_id: Cell<TypeVarId>,
35
32 - bindings: Rc<RefCell<HashMap<(String, BindingId), Identifier>>>,
36 + bindings: Rc<RefCell<HashMap<DeclarationId, Identifier>>>,
37 }
38
39 impl Environment {
36 - pub fn new(features: Features, registry: Registry) -> Self {
40 + pub fn new(features: Features, registry: Registry, analysis: ScopeManager) -> Self {
41 Self {
42 features,
43 registry,
44 + analysis,
45 next_block_id: Cell::new(BlockId(0)),
46 next_identifier_id: Cell::new(IdentifierId(0)),
47 next_type_var_id: Cell::new(TypeVarId(0)),
@@ -65,24 +70,46 @@ impl Environment {
70 id
71 }
72
68 - pub fn resolve_binding_identifier(&self, name: &str, binding_id: BindingId) -> Identifier {
69 - let key_name = name.to_string();
73 + pub fn resolve_variable_declaration<T: ESTreeNode>(
74 + &self,
75 + node: &T,
76 + name: &str,
77 + ) -> Option<Identifier> {
78 + let declaration = self.analysis.node_declaration(node)?;
79 let mut bindings = self.bindings.borrow_mut();
71 - if let Some(identifier) = bindings.get(&(key_name.clone(), binding_id)) {
72 - identifier.clone()
80 + if let Some(identifier) = bindings.get(&declaration.id) {
81 + Some(identifier.clone())
82 } else {
83 let id = self.next_identifier_id();
84 let identifier = Identifier {
85 id,
77 - name: Some(key_name.clone()),
86 + name: Some(name.to_string()),
87 data: Rc::new(RefCell::new(IdentifierData {
88 mutable_range: Default::default(),
89 scope: None,
90 type_: Type::Var(self.next_type_var_id()),
91 })),
92 };
84 - bindings.insert((key_name, binding_id), identifier.clone());
85 - identifier
93 + bindings.insert(declaration.id, identifier.clone());
94 + Some(identifier)
95 }
96 }
97 +
98 + pub fn resolve_variable_reference<T: ESTreeNode>(&self, node: &T) -> Option<Identifier> {
99 + let reference = self.analysis.node_reference(node)?;
100 + let bindings = self.bindings.borrow();
101 + let declaration = self.analysis.declaration(reference.declaration);
102 + let identifier = bindings.get(&declaration.id)?;
103 + Some(identifier.clone())
104 + }
105 +
106 + pub fn resolve_declaration_id(&self, id: DeclarationId) -> Option<Identifier> {
107 + let bindings = self.bindings.borrow();
108 + let identifier = bindings.get(&id)?;
109 + Some(identifier.clone())
110 + }
111 +
112 + pub fn scope<T: ESTreeNode>(&self, node: &T) -> Option<ScopeView<'_>> {
113 + self.analysis.node_scope_view(node)
114 + }
115 }
compiler/forget/crates/forget_semantic_analysis/src/analyzer.rs
+12 -7
@@ -1,8 +1,9 @@
1 use forget_diagnostics::Diagnostic;
2 use forget_estree::{
3 AssignmentOperator, AssignmentPropertyOrRestElement, AssignmentTarget, Expression,
4 - ExpressionOrSuper, ForInInit, ForInit, Function, FunctionBody, Identifier, JSXElementName,
5 - Pattern, Program, SourceRange, SourceType, Statement, VariableDeclarationKind, Visitor,
4 + ExpressionOrSuper, ForInInit, ForInit, Function, FunctionBody, Identifier, IntoFunction,
5 + JSXElementName, Pattern, Program, SourceRange, SourceType, Statement, VariableDeclarationKind,
6 + Visitor,
7 };
8
9 use crate::{AstNode, DeclarationKind, LabelKind, ReferenceKind, ScopeId, ScopeKind, ScopeManager};
@@ -48,8 +49,9 @@ impl Analyzer {
49 self.current = scope.parent.unwrap();
50 }
51
51 - fn visit_function(&mut self, function: &Function) {
52 - self.enter(ScopeKind::Function, |visitor| {
52 + fn visit_function<T: IntoFunction>(&mut self, node: &T) {
53 + let function = node.function();
54 + let scope = self.enter(ScopeKind::Function, |visitor| {
55 for param in &function.params {
56 // `this` parameters don't declare variables, nor can they have
57 // default values
@@ -80,6 +82,9 @@ impl Analyzer {
82 }
83 }
84 });
85 + self.manager
86 + .node_scopes
87 + .insert(AstNode::from(function), scope);
88 }
89
90 fn visit_reference_identifier(
@@ -230,7 +235,7 @@ impl Visitor for Analyzer {
235 .node_declarations
236 .insert(AstNode::from(id), declaration);
237 }
233 - Analyzer::visit_function(self, &ast.function);
238 + Analyzer::visit_function(self, ast);
239 }
240
241 fn visit_function_expression(&mut self, ast: &forget_estree::FunctionExpression) {
@@ -247,14 +252,14 @@ impl Visitor for Analyzer {
252 .insert(AstNode::from(id), declaration);
253 }
254
250 - Analyzer::visit_function(self, &ast.function);
255 + Analyzer::visit_function(self, ast);
256 if let Some(function_scope) = function_scope {
257 self.close_scope(function_scope);
258 }
259 }
260
261 fn visit_arrow_function_expression(&mut self, ast: &forget_estree::ArrowFunctionExpression) {
257 - Analyzer::visit_function(self, &ast.function);
262 + Analyzer::visit_function(self, ast);
263 }
264
265 fn visit_assignment_expression(&mut self, ast: &forget_estree::AssignmentExpression) {
compiler/forget/crates/forget_semantic_analysis/src/lib.rs
+1
@@ -4,3 +4,4 @@ mod scope_view;
4
5 pub use analyzer::analyze;
6 pub use scope_manager::*;
7 +pub use scope_view::*;
compiler/forget/crates/forget_semantic_analysis/src/scope_manager.rs
+54 -7
@@ -1,12 +1,11 @@
1 use forget_diagnostics::Diagnostic;
2 use forget_estree::{
3 - BreakStatement, ContinueStatement, ESTreeNode, Identifier, LabeledStatement,
4 - VariableDeclarationKind,
3 + BreakStatement, ContinueStatement, ESTreeNode, LabeledStatement, VariableDeclarationKind,
4 };
5 use forget_utils::PointerAddress;
6 use indexmap::IndexMap;
7
9 -use crate::scope_view::ScopeView;
8 +use crate::scope_view::{DeclarationView, ReferenceView, ScopeView};
9
10 pub struct ScopeManager {
11 root: ScopeId,
@@ -26,6 +25,12 @@ pub struct ScopeManager {
25 pub(crate) diagnostics: Vec<Diagnostic>,
26 }
27
28 +impl std::fmt::Debug for ScopeManager {
29 + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30 + std::fmt::Debug::fmt(&self.debug(), f)
31 + }
32 +}
33 +
34 impl ScopeManager {
35 pub(crate) fn new() -> Self {
36 let root_id = ScopeId(0);
@@ -71,6 +76,21 @@ impl ScopeManager {
76 &self.scopes[id.0]
77 }
78
79 + pub fn is_descendant_of(&self, maybe_descendant: ScopeId, maybe_ancestor: ScopeId) -> bool {
80 + let mut current = maybe_descendant;
81 + loop {
82 + if current == maybe_ancestor {
83 + return true;
84 + }
85 + let scope = self.scope(current);
86 + if let Some(parent) = scope.parent {
87 + current = parent;
88 + } else {
89 + return false;
90 + }
91 + }
92 + }
93 +
94 pub fn label(&self, id: LabelId) -> &Label {
95 &self.labels[id.0]
96 }
@@ -89,6 +109,15 @@ impl ScopeManager {
109 .map(|id| &self.scopes[id.0])
110 }
111
112 + pub fn node_scope_view<T: ESTreeNode>(&self, node: &T) -> Option<ScopeView<'_>> {
113 + self.node_scopes
114 + .get(&AstNode::from(node))
115 + .map(|id| ScopeView {
116 + manager: self,
117 + scope: &self.scopes[id.0],
118 + })
119 + }
120 +
121 pub fn node_label(&self, node: &LabeledStatement) -> Option<&Label> {
122 self.node_labels
123 .get(&AstNode::from(node))
@@ -107,18 +136,36 @@ impl ScopeManager {
136 .map(|id| &self.labels[id.0])
137 }
138
110 - pub fn node_declaration(&self, node: &Identifier) -> Option<&Declaration> {
139 + pub fn node_declaration<T: ESTreeNode>(&self, node: &T) -> Option<&Declaration> {
140 self.node_declarations
141 .get(&AstNode::from(node))
142 .map(|id| &self.declarations[id.0])
143 }
144
116 - pub fn node_reference(&self, node: &Identifier) -> Option<&Reference> {
145 + pub fn node_declaration_view<T: ESTreeNode>(&self, node: &T) -> Option<DeclarationView<'_>> {
146 + self.node_declarations
147 + .get(&AstNode::from(node))
148 + .map(|id| DeclarationView {
149 + manager: self,
150 + declaration: &self.declarations[id.0],
151 + })
152 + }
153 +
154 + pub fn node_reference<T: ESTreeNode>(&self, node: &T) -> Option<&Reference> {
155 self.node_references
156 .get(&AstNode::from(node))
157 .map(|id| &self.references[id.0])
158 }
159
160 + pub fn node_reference_view<T: ESTreeNode>(&self, node: &T) -> Option<ReferenceView<'_>> {
161 + self.node_references
162 + .get(&AstNode::from(node))
163 + .map(|id| ReferenceView {
164 + manager: self,
165 + reference: &self.references[id.0],
166 + })
167 + }
168 +
169 pub fn lookup_label(&self, scope: ScopeId, name: &str) -> Option<&Label> {
170 let mut current = &self.scopes[scope.0];
171 loop {
@@ -316,10 +363,10 @@ pub struct Reference {
363 }
364
365 #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
319 -pub(crate) struct AstNode(PointerAddress);
366 +pub struct AstNode(PointerAddress);
367
368 impl AstNode {
322 - fn new<T: ESTreeNode>(node: &T) -> Self {
369 + pub fn new<T: ESTreeNode>(node: &T) -> Self {
370 Self(PointerAddress::new(node))
371 }
372 }
compiler/forget/crates/forget_semantic_analysis/src/scope_view.rs
+132 -7
@@ -1,12 +1,86 @@
1 use indexmap::IndexMap;
2
3 -use crate::{Declaration, Label, Reference, Scope, ScopeManager};
3 +use crate::{
4 + Declaration, DeclarationId, DeclarationKind, Label, Reference, ReferenceId, ReferenceKind,
5 + Scope, ScopeId, ScopeKind, ScopeManager,
6 +};
7
8 +#[derive(Clone, Copy)]
9 pub struct ScopeView<'m> {
10 pub(crate) manager: &'m ScopeManager,
11 pub(crate) scope: &'m Scope,
12 }
13
14 +impl<'m> ScopeView<'m> {
15 + pub fn id(&self) -> ScopeId {
16 + self.scope.id
17 + }
18 +
19 + pub fn kind(&self) -> ScopeKind {
20 + self.scope.kind
21 + }
22 +
23 + pub fn parent(&self) -> Option<ScopeView<'m>> {
24 + self.scope.parent.map(|id| {
25 + let scope = self.manager.scope(id);
26 + ScopeView {
27 + manager: &self.manager,
28 + scope,
29 + }
30 + })
31 + }
32 +
33 + pub fn declarations(&self) -> Vec<DeclarationView<'m>> {
34 + self.scope
35 + .declarations
36 + .values()
37 + .cloned()
38 + .map(|id| {
39 + let declaration = self.manager.declaration(id);
40 + DeclarationView {
41 + manager: &self.manager,
42 + declaration,
43 + }
44 + })
45 + .collect()
46 + }
47 +
48 + pub fn references(&self) -> Vec<ReferenceView<'m>> {
49 + self.scope
50 + .references
51 + .iter()
52 + .cloned()
53 + .map(|id| {
54 + let reference = self.manager.reference(id);
55 + ReferenceView {
56 + manager: &self.manager,
57 + reference,
58 + }
59 + })
60 + .collect()
61 + }
62 +
63 + pub fn children(&self) -> Vec<ScopeView<'m>> {
64 + self.scope
65 + .children
66 + .iter()
67 + .cloned()
68 + .map(|id| {
69 + let scope = self.manager.scope(id);
70 + ScopeView {
71 + manager: &self.manager,
72 + scope,
73 + }
74 + })
75 + .collect()
76 + }
77 +
78 + pub fn is_descendant_of(&self, maybe_ancestor: Self) -> bool {
79 + self.manager
80 + .is_descendant_of(self.scope.id, maybe_ancestor.scope.id)
81 + }
82 +}
83 +
84 impl<'m> std::fmt::Debug for ScopeView<'m> {
85 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86 let labels: IndexMap<_, _> = self
@@ -66,9 +140,10 @@ impl<'m> std::fmt::Debug for ScopeView<'m> {
140 }
141 }
142
143 +#[derive(Clone, Copy)]
144 pub struct LabelView<'m> {
70 - manager: &'m ScopeManager,
71 - label: &'m Label,
145 + pub(crate) manager: &'m ScopeManager,
146 + pub(crate) label: &'m Label,
147 }
148
149 impl<'m> std::fmt::Debug for LabelView<'m> {
@@ -81,9 +156,32 @@ impl<'m> std::fmt::Debug for LabelView<'m> {
156 }
157 }
158
159 +#[derive(Clone, Copy)]
160 pub struct DeclarationView<'m> {
85 - manager: &'m ScopeManager,
86 - declaration: &'m Declaration,
161 + pub(crate) manager: &'m ScopeManager,
162 + pub(crate) declaration: &'m Declaration,
163 +}
164 +
165 +impl<'m> DeclarationView<'m> {
166 + pub fn id(&self) -> DeclarationId {
167 + self.declaration.id
168 + }
169 +
170 + pub fn name(&self) -> &str {
171 + &self.declaration.name
172 + }
173 +
174 + pub fn kind(&self) -> DeclarationKind {
175 + self.declaration.kind
176 + }
177 +
178 + pub fn scope(&self) -> ScopeView<'m> {
179 + let scope = self.manager.scope(self.declaration.scope);
180 + ScopeView {
181 + manager: &self.manager,
182 + scope,
183 + }
184 + }
185 }
186
187 impl<'m> std::fmt::Debug for DeclarationView<'m> {
@@ -96,9 +194,36 @@ impl<'m> std::fmt::Debug for DeclarationView<'m> {
194 }
195 }
196
197 +#[derive(Clone, Copy)]
198 pub struct ReferenceView<'m> {
100 - manager: &'m ScopeManager,
101 - reference: &'m Reference,
199 + pub(crate) manager: &'m ScopeManager,
200 + pub(crate) reference: &'m Reference,
201 +}
202 +
203 +impl<'m> ReferenceView<'m> {
204 + pub fn id(&self) -> ReferenceId {
205 + self.reference.id
206 + }
207 +
208 + pub fn kind(&self) -> ReferenceKind {
209 + self.reference.kind
210 + }
211 +
212 + pub fn scope(&self) -> ScopeView<'m> {
213 + let scope = self.manager.scope(self.reference.scope);
214 + ScopeView {
215 + manager: &self.manager,
216 + scope,
217 + }
218 + }
219 +
220 + pub fn declaration(&self) -> DeclarationView<'m> {
221 + let declaration = self.manager.declaration(self.reference.declaration);
222 + DeclarationView {
223 + manager: &self.manager,
224 + declaration,
225 + }
226 + }
227 }
228
229 impl<'m> std::fmt::Debug for ReferenceView<'m> {
compiler/forget/crates/forget_semantic_analysis/tests/analysis_test.rs
+1 -4
@@ -12,15 +12,12 @@ fn fixtures() {
12 let ast = parse(&input, path.to_str().unwrap()).unwrap();
13 let mut analysis = analyze(&ast);
14
15 - let ast_output = serde_json::to_string_pretty(&ast).unwrap();
15 let mut output = String::new();
16 writeln!(&mut output, "{:#?}", analysis.debug()).unwrap();
17 let diagnostics = analysis.diagnostics();
18 for diagnostic in diagnostics {
19 writeln!(&mut output, "{:#?}", diagnostic).unwrap();
20 }
22 - assert_snapshot!(format!(
23 - "Input:\n{input}\n\nAST:\n{ast_output}\n\nAnalysis:\n{output}"
24 - ));
21 + assert_snapshot!(format!("Input:\n{input}\n\nAnalysis:\n{output}"));
22 });
23 }
compiler/forget/crates/forget_semantic_analysis/tests/fixtures/simple-function.js new
+9
@@ -0,0 +1,9 @@
1 +function Component(a) {
2 + Math;
3 + let b = 0;
4 + const foo = function foo_(c) {
5 + let d = 1;
6 + return a + b + c + d;
7 + };
8 + return foo();
9 +}
compiler/forget/crates/forget_semantic_analysis/tests/snapshots/analysis_test__fixtures@globals-and-imports.js.snap
+1 -786
@@ -1,6 +1,6 @@
1 ---
2 source: crates/forget_semantic_analysis/tests/analysis_test.rs
3 -expression: "format!(\"Input:\\n{input}\\n\\nAST:\\n{ast_output}\\n\\nAnalysis:\\n{output}\")"
3 +expression: "format!(\"Input:\\n{input}\\n\\nAnalysis:\\n{output}\")"
4 input_file: crates/forget_semantic_analysis/tests/fixtures/globals-and-imports.js
5 ---
6 Input:
@@ -23,791 +23,6 @@ function Component(props) {
23 }
24
25
26 -AST:
27 -{
28 - "type": "Program",
29 - "body": [
30 - {
31 - "type": "ImportDeclaration",
32 - "specifiers": [
33 - {
34 - "type": "ImportDefaultSpecifier",
35 - "local": {
36 - "type": "Identifier",
37 - "name": "Foo",
38 - "typeAnnotation": null,
39 - "loc": null,
40 - "range": {
41 - "start": 0,
42 - "end": 1
43 - }
44 - },
45 - "loc": null,
46 - "range": {
47 - "start": 0,
48 - "end": 1
49 - }
50 - }
51 - ],
52 - "source": {
53 - "type": "StringLiteral",
54 - "value": "foo",
55 - "loc": null,
56 - "range": {
57 - "start": 0,
58 - "end": 1
59 - }
60 - },
61 - "loc": null,
62 - "range": {
63 - "start": 0,
64 - "end": 1
65 - }
66 - },
67 - {
68 - "type": "ImportDeclaration",
69 - "specifiers": [
70 - {
71 - "type": "ImportNamespaceSpecifier",
72 - "local": {
73 - "type": "Identifier",
74 - "name": "Bar",
75 - "typeAnnotation": null,
76 - "loc": null,
77 - "range": {
78 - "start": 0,
79 - "end": 1
80 - }
81 - },
82 - "loc": null,
83 - "range": {
84 - "start": 0,
85 - "end": 1
86 - }
87 - }
88 - ],
89 - "source": {
90 - "type": "StringLiteral",
91 - "value": "bar",
92 - "loc": null,
93 - "range": {
94 - "start": 0,
95 - "end": 1
96 - }
97 - },
98 - "loc": null,
99 - "range": {
100 - "start": 0,
101 - "end": 1
102 - }
103 - },
104 - {
105 - "type": "ImportDeclaration",
106 - "specifiers": [
107 - {
108 - "type": "ImportSpecifier",
109 - "imported": {
110 - "type": "Identifier",
111 - "name": "Baz",
112 - "typeAnnotation": null,
113 - "loc": null,
114 - "range": {
115 - "start": 0,
116 - "end": 1
117 - }
118 - },
119 - "local": {
120 - "type": "Identifier",
121 - "name": "Baz",
122 - "typeAnnotation": null,
123 - "loc": null,
124 - "range": {
125 - "start": 0,
126 - "end": 1
127 - }
128 - },
129 - "loc": null,
130 - "range": {
131 - "start": 0,
132 - "end": 1
133 - }
134 - }
135 - ],
136 - "source": {
137 - "type": "StringLiteral",
138 - "value": "baz",
139 - "loc": null,
140 - "range": {
141 - "start": 0,
142 - "end": 1
143 - }
144 - },
145 - "loc": null,
146 - "range": {
147 - "start": 0,
148 - "end": 1
149 - }
150 - },
151 - {
152 - "type": "FunctionDeclaration",
153 - "id": {
154 - "type": "Identifier",
155 - "name": "Component",
156 - "typeAnnotation": null,
157 - "loc": null,
158 - "range": {
159 - "start": 0,
160 - "end": 1
161 - }
162 - },
163 - "params": [
164 - {
165 - "type": "Identifier",
166 - "name": "props",
167 - "typeAnnotation": null,
168 - "loc": null,
169 - "range": {
170 - "start": 0,
171 - "end": 1
172 - }
173 - }
174 - ],
175 - "body": {
176 - "type": "BlockStatement",
177 - "body": [
178 - {
179 - "type": "VariableDeclaration",
180 - "kind": "let",
181 - "declarations": [
182 - {
183 - "type": "VariableDeclarator",
184 - "id": {
185 - "type": "Identifier",
186 - "name": "g",
187 - "typeAnnotation": null,
188 - "loc": null,
189 - "range": {
190 - "start": 0,
191 - "end": 1
192 - }
193 - },
194 - "init": {
195 - "type": "Identifier",
196 - "name": "global",
197 - "typeAnnotation": null,
198 - "loc": null,
199 - "range": {
200 - "start": 0,
201 - "end": 1
202 - }
203 - },
204 - "loc": null,
205 - "range": {
206 - "start": 0,
207 - "end": 1
208 - }
209 - }
210 - ],
211 - "loc": null,
212 - "range": {
213 - "start": 0,
214 - "end": 1
215 - }
216 - },
217 - {
218 - "type": "VariableDeclaration",
219 - "kind": "let",
220 - "declarations": [
221 - {
222 - "type": "VariableDeclarator",
223 - "id": {
224 - "type": "Identifier",
225 - "name": "y",
226 - "typeAnnotation": null,
227 - "loc": null,
228 - "range": {
229 - "start": 0,
230 - "end": 1
231 - }
232 - },
233 - "init": {
234 - "type": "NewExpression",
235 - "callee": {
236 - "type": "Identifier",
237 - "name": "Array",
238 - "typeAnnotation": null,
239 - "loc": null,
240 - "range": {
241 - "start": 0,
242 - "end": 1
243 - }
244 - },
245 - "arguments": [
246 - {
247 - "type": "MemberExpression",
248 - "object": {
249 - "type": "Identifier",
250 - "name": "props",
251 - "typeAnnotation": null,
252 - "loc": null,
253 - "range": {
254 - "start": 0,
255 - "end": 1
256 - }
257 - },
258 - "property": {
259 - "type": "Identifier",
260 - "name": "count",
261 - "typeAnnotation": null,
262 - "loc": null,
263 - "range": {
264 - "start": 0,
265 - "end": 1
266 - }
267 - },
268 - "computed": false,
269 - "loc": null,
270 - "range": {
271 - "start": 0,
272 - "end": 1
273 - }
274 - }
275 - ],
276 - "loc": null,
277 - "range": {
278 - "start": 0,
279 - "end": 1
280 - }
281 - },
282 - "loc": null,
283 - "range": {
284 - "start": 0,
285 - "end": 1
286 - }
287 - }
288 - ],
289 - "loc": null,
290 - "range": {
291 - "start": 0,
292 - "end": 1
293 - }
294 - },
295 - {
296 - "type": "VariableDeclaration",
297 - "kind": "let",
298 - "declarations": [
299 - {
300 - "type": "VariableDeclarator",
301 - "id": {
302 - "type": "Identifier",
303 - "name": "s",
304 - "typeAnnotation": null,
305 - "loc": null,
306 - "range": {
307 - "start": 0,
308 - "end": 1
309 - }
310 - },
311 - "init": {
312 - "type": "CallExpression",
313 - "callee": {
314 - "type": "Identifier",
315 - "name": "String",
316 - "typeAnnotation": null,
317 - "loc": null,
318 - "range": {
319 - "start": 0,
320 - "end": 1
321 - }
322 - },
323 - "arguments": [
324 - {
325 - "type": "StringLiteral",
326 - "value": "hello",
327 - "loc": null,
328 - "range": {
329 - "start": 0,
330 - "end": 1
331 - }
332 - }
333 - ],
334 - "loc": null,
335 - "range": {
336 - "start": 0,
337 - "end": 1
338 - }
339 - },
340 - "loc": null,
341 - "range": {
342 - "start": 0,
343 - "end": 1
344 - }
345 - }
346 - ],
347 - "loc": null,
348 - "range": {
349 - "start": 0,
350 - "end": 1
351 - }
352 - },
353 - {
354 - "type": "VariableDeclaration",
355 - "kind": "let",
356 - "declarations": [
357 - {
358 - "type": "VariableDeclarator",
359 - "id": {
360 - "type": "Identifier",
361 - "name": "b",
362 - "typeAnnotation": null,
363 - "loc": null,
364 - "range": {
365 - "start": 0,
366 - "end": 1
367 - }
368 - },
369 - "init": {
370 - "type": "CallExpression",
371 - "callee": {
372 - "type": "Identifier",
373 - "name": "Boolean",
374 - "typeAnnotation": null,
375 - "loc": null,
376 - "range": {
377 - "start": 0,
378 - "end": 1
379 - }
380 - },
381 - "arguments": [
382 - {
383 - "type": "BooleanLiteral",
384 - "value": true,
385 - "loc": null,
386 - "range": {
387 - "start": 0,
388 - "end": 1
389 - }
390 - }
391 - ],
392 - "loc": null,
393 - "range": {
394 - "start": 0,
395 - "end": 1
396 - }
397 - },
398 - "loc": null,
399 - "range": {
400 - "start": 0,
401 - "end": 1
402 - }
403 - }
404 - ],
405 - "loc": null,
406 - "range": {
407 - "start": 0,
408 - "end": 1
409 - }
410 - },
411 - {
412 - "type": "VariableDeclaration",
413 - "kind": "let",
414 - "declarations": [
415 - {
416 - "type": "VariableDeclarator",
417 - "id": {
418 - "type": "Identifier",
419 - "name": "n",
420 - "typeAnnotation": null,
421 - "loc": null,
422 - "range": {
423 - "start": 0,
424 - "end": 1
425 - }
426 - },
427 - "init": {
428 - "type": "CallExpression",
429 - "callee": {
430 - "type": "Identifier",
431 - "name": "Number",
432 - "typeAnnotation": null,
433 - "loc": null,
434 - "range": {
435 - "start": 0,
436 - "end": 1
437 - }
438 - },
439 - "arguments": [
440 - {
441 - "type": "NumericLiteral",
442 - "value": 0,
443 - "loc": null,
444 - "range": {
445 - "start": 0,
446 - "end": 1
447 - }
448 - }
449 - ],
450 - "loc": null,
451 - "range": {
452 - "start": 0,
453 - "end": 1
454 - }
455 - },
456 - "loc": null,
457 - "range": {
458 - "start": 0,
459 - "end": 1
460 - }
461 - }
462 - ],
463 - "loc": null,
464 - "range": {
465 - "start": 0,
466 - "end": 1
467 - }
468 - },
469 - {
470 - "type": "VariableDeclaration",
471 - "kind": "let",
472 - "declarations": [
473 - {
474 - "type": "VariableDeclarator",
475 - "id": {
476 - "type": "Identifier",
477 - "name": "x",
478 - "typeAnnotation": null,
479 - "loc": null,
480 - "range": {
481 - "start": 0,
482 - "end": 1
483 - }
484 - },
485 - "init": {
486 - "type": "CallExpression",
487 - "callee": {
488 - "type": "MemberExpression",
489 - "object": {
490 - "type": "Identifier",
491 - "name": "Math",
492 - "typeAnnotation": null,
493 - "loc": null,
494 - "range": {
495 - "start": 0,
496 - "end": 1
497 - }
498 - },
499 - "property": {
500 - "type": "Identifier",
501 - "name": "min",
502 - "typeAnnotation": null,
503 - "loc": null,
504 - "range": {
505 - "start": 0,
506 - "end": 1
507 - }
508 - },
509 - "computed": false,
510 - "loc": null,
511 - "range": {
512 - "start": 0,
513 - "end": 1
514 - }
515 - },
516 - "arguments": [
517 - {
518 - "type": "MemberExpression",
519 - "object": {
520 - "type": "Identifier",
521 - "name": "props",
522 - "typeAnnotation": null,
523 - "loc": null,
524 - "range": {
525 - "start": 0,
526 - "end": 1
527 - }
528 - },
529 - "property": {
530 - "type": "Identifier",
531 - "name": "x",
532 - "typeAnnotation": null,
533 - "loc": null,
534 - "range": {
535 - "start": 0,
536 - "end": 1
537 - }
538 - },
539 - "computed": false,
540 - "loc": null,
541 - "range": {
542 - "start": 0,
543 - "end": 1
544 - }
545 - },
546 - {
547 - "type": "MemberExpression",
548 - "object": {
549 - "type": "Identifier",
550 - "name": "props",
551 - "typeAnnotation": null,
552 - "loc": null,
553 - "range": {
554 - "start": 0,
555 - "end": 1
556 - }
557 - },
558 - "property": {
559 - "type": "Identifier",
560 - "name": "y",
561 - "typeAnnotation": null,
562 - "loc": null,
563 - "range": {
564 - "start": 0,
565 - "end": 1
566 - }
567 - },
568 - "computed": false,
569 - "loc": null,
570 - "range": {
571 - "start": 0,
572 - "end": 1
573 - }
574 - }
575 - ],
576 - "loc": null,
577 - "range": {
578 - "start": 0,
579 - "end": 1
580 - }
581 - },
582 - "loc": null,
583 - "range": {
584 - "start": 0,
585 - "end": 1
586 - }
587 - }
588 - ],
589 - "loc": null,
590 - "range": {
591 - "start": 0,
592 - "end": 1
593 - }
594 - },
595 - {
596 - "type": "ExpressionStatement",
597 - "expression": {
598 - "type": "CallExpression",
599 - "callee": {
600 - "type": "Identifier",
601 - "name": "setTimeout",
602 - "typeAnnotation": null,
603 - "loc": null,
604 - "range": {
605 - "start": 0,
606 - "end": 1
607 - }
608 - },
609 - "arguments": [
610 - {
611 - "type": "ArrowFunctionExpression",
612 - "id": null,
613 - "params": [],
614 - "body": {
615 - "type": "BlockStatement",
616 - "body": [],
617 - "loc": null,
618 - "range": {
619 - "start": 0,
620 - "end": 1
621 - }
622 - },
623 - "generator": false,
624 - "async": false,
625 - "loc": null,
626 - "range": {
627 - "start": 0,
628 - "end": 1
629 - },
630 - "expression": false,
631 - "loc": null,
632 - "range": {
633 - "start": 0,
634 - "end": 1
635 - }
636 - },
637 - {
638 - "type": "NumericLiteral",
639 - "value": 0,
640 - "loc": null,
641 - "range": {
642 - "start": 0,
643 - "end": 1
644 - }
645 - }
646 - ],
647 - "loc": null,
648 - "range": {
649 - "start": 0,
650 - "end": 1
651 - }
652 - },
653 - "directive": null,
654 - "loc": null,
655 - "range": {
656 - "start": 0,
657 - "end": 1
658 - }
659 - },
660 - {
661 - "type": "ExpressionStatement",
662 - "expression": {
663 - "type": "CallExpression",
664 - "callee": {
665 - "type": "Identifier",
666 - "name": "setInterval",
667 - "typeAnnotation": null,
668 - "loc": null,
669 - "range": {
670 - "start": 0,
671 - "end": 1
672 - }
673 - },
674 - "arguments": [
675 - {
676 - "type": "ArrowFunctionExpression",
677 - "id": null,
678 - "params": [],
679 - "body": {
680 - "type": "BlockStatement",
681 - "body": [],
682 - "loc": null,
683 - "range": {
684 - "start": 0,
685 - "end": 1
686 - }
687 - },
688 - "generator": false,
689 - "async": false,
690 - "loc": null,
691 - "range": {
692 - "start": 0,
693 - "end": 1
694 - },
695 - "expression": false,
696 - "loc": null,
697 - "range": {
698 - "start": 0,
699 - "end": 1
700 - }
701 - },
702 - {
703 - "type": "NumericLiteral",
704 - "value": 0,
705 - "loc": null,
706 - "range": {
707 - "start": 0,
708 - "end": 1
709 - }
710 - }
711 - ],
712 - "loc": null,
713 - "range": {
714 - "start": 0,
715 - "end": 1
716 - }
717 - },
718 - "directive": null,
719 - "loc": null,
720 - "range": {
721 - "start": 0,
722 - "end": 1
723 - }
724 - },
725 - {
726 - "type": "ExpressionStatement",
727 - "expression": {
728 - "type": "Identifier",
729 - "name": "Foo",
730 - "typeAnnotation": null,
731 - "loc": null,
732 - "range": {
733 - "start": 0,
734 - "end": 1
735 - }
736 - },
737 - "directive": null,
738 - "loc": null,
739 - "range": {
740 - "start": 0,
741 - "end": 1
742 - }
743 - },
744 - {
745 - "type": "ExpressionStatement",
746 - "expression": {
747 - "type": "Identifier",
748 - "name": "Bar",
749 - "typeAnnotation": null,
750 - "loc": null,
751 - "range": {
752 - "start": 0,
753 - "end": 1
754 - }
755 - },
756 - "directive": null,
757 - "loc": null,
758 - "range": {
759 - "start": 0,
760 - "end": 1
761 - }
762 - },
763 - {
764 - "type": "ExpressionStatement",
765 - "expression": {
766 - "type": "Identifier",
767 - "name": "Baz",
768 - "typeAnnotation": null,
769 - "loc": null,
770 - "range": {
771 - "start": 0,
772 - "end": 1
773 - }
774 - },
775 - "directive": null,
776 - "loc": null,
777 - "range": {
778 - "start": 0,
779 - "end": 1
780 - }
781 - }
782 - ],
783 - "loc": null,
784 - "range": {
785 - "start": 0,
786 - "end": 1
787 - }
788 - },
789 - "generator": false,
790 - "async": false,
791 - "loc": null,
792 - "range": {
793 - "start": 0,
794 - "end": 1
795 - },
796 - "loc": null,
797 - "range": {
798 - "start": 0,
799 - "end": 1
800 - }
801 - }
802 - ],
803 - "sourceType": "module",
804 - "loc": null,
805 - "range": {
806 - "start": 0,
807 - "end": 1
808 - }
809 -}
810 -
26 Analysis:
27 Scope {
28 id: ScopeId(
compiler/forget/crates/forget_semantic_analysis/tests/snapshots/analysis_test__fixtures@labels.js.snap
+1 -426
@@ -1,6 +1,6 @@
1 ---
2 source: crates/forget_semantic_analysis/tests/analysis_test.rs
3 -expression: "format!(\"Input:\\n{input}\\n\\nAST:\\n{ast_output}\\n\\nAnalysis:\\n{output}\")"
3 +expression: "format!(\"Input:\\n{input}\\n\\nAnalysis:\\n{output}\")"
4 input_file: crates/forget_semantic_analysis/tests/fixtures/labels.js
5 ---
6 Input:
@@ -19,431 +19,6 @@ function Component(props) {
19 }
20
21
22 -AST:
23 -{
24 - "type": "Program",
25 - "body": [
26 - {
27 - "type": "FunctionDeclaration",
28 - "id": {
29 - "type": "Identifier",
30 - "name": "Component",
31 - "typeAnnotation": null,
32 - "loc": null,
33 - "range": {
34 - "start": 0,
35 - "end": 1
36 - }
37 - },
38 - "params": [
39 - {
40 - "type": "Identifier",
41 - "name": "props",
42 - "typeAnnotation": null,
43 - "loc": null,
44 - "range": {
45 - "start": 0,
46 - "end": 1
47 - }
48 - }
49 - ],
50 - "body": {
51 - "type": "BlockStatement",
52 - "body": [
53 - {
54 - "type": "VariableDeclaration",
55 - "kind": "let",
56 - "declarations": [
57 - {
58 - "type": "VariableDeclarator",
59 - "id": {
60 - "type": "Identifier",
61 - "name": "y",
62 - "typeAnnotation": null,
63 - "loc": null,
64 - "range": {
65 - "start": 0,
66 - "end": 1
67 - }
68 - },
69 - "init": {
70 - "type": "NumericLiteral",
71 - "value": 0,
72 - "loc": null,
73 - "range": {
74 - "start": 0,
75 - "end": 1
76 - }
77 - },
78 - "loc": null,
79 - "range": {
80 - "start": 0,
81 - "end": 1
82 - }
83 - }
84 - ],
85 - "loc": null,
86 - "range": {
87 - "start": 0,
88 - "end": 1
89 - }
90 - },
91 - {
92 - "type": "LabeledStatement",
93 - "label": {
94 - "type": "Identifier",
95 - "name": "foo",
96 - "typeAnnotation": null,
97 - "loc": null,
98 - "range": {
99 - "start": 0,
100 - "end": 1
101 - }
102 - },
103 - "body": {
104 - "type": "ForStatement",
105 - "init": {
106 - "type": "VariableDeclaration",
107 - "kind": "let",
108 - "declarations": [
109 - {
110 - "type": "VariableDeclarator",
111 - "id": {
112 - "type": "Identifier",
113 - "name": "x",
114 - "typeAnnotation": null,
115 - "loc": null,
116 - "range": {
117 - "start": 0,
118 - "end": 1
119 - }
120 - },
121 - "init": {
122 - "type": "NumericLiteral",
123 - "value": 0,
124 - "loc": null,
125 - "range": {
126 - "start": 0,
127 - "end": 1
128 - }
129 - },
130 - "loc": null,
131 - "range": {
132 - "start": 0,
133 - "end": 1
134 - }
135 - }
136 - ],
137 - "loc": null,
138 - "range": {
139 - "start": 0,
140 - "end": 1
141 - }
142 - },
143 - "test": {
144 - "type": "BinaryExpression",
145 - "left": {
146 - "type": "Identifier",
147 - "name": "x",
148 - "typeAnnotation": null,
149 - "loc": null,
150 - "range": {
151 - "start": 0,
152 - "end": 1
153 - }
154 - },
155 - "operator": "<",
156 - "right": {
157 - "type": "NumericLiteral",
158 - "value": 4621819117588971520,
159 - "loc": null,
160 - "range": {
161 - "start": 0,
162 - "end": 1
163 - }
164 - },
165 - "loc": null,
166 - "range": {
167 - "start": 0,
168 - "end": 1
169 - }
170 - },
171 - "update": {
172 - "type": "UpdateExpression",
173 - "operator": "++",
174 - "argument": {
175 - "type": "Identifier",
176 - "name": "x",
177 - "typeAnnotation": null,
178 - "loc": null,
179 - "range": {
180 - "start": 0,
181 - "end": 1
182 - }
183 - },
184 - "prefix": false,
185 - "loc": null,
186 - "range": {
187 - "start": 0,
188 - "end": 1
189 - }
190 - },
191 - "body": {
192 - "type": "BlockStatement",
193 - "body": [
194 - {
195 - "type": "IfStatement",
196 - "test": {
197 - "type": "BinaryExpression",
198 - "left": {
199 - "type": "Identifier",
200 - "name": "x",
201 - "typeAnnotation": null,
202 - "loc": null,
203 - "range": {
204 - "start": 0,
205 - "end": 1
206 - }
207 - },
208 - "operator": "==",
209 - "right": {
210 - "type": "NumericLiteral",
211 - "value": 4619567317775286272,
212 - "loc": null,
213 - "range": {
214 - "start": 0,
215 - "end": 1
216 - }
217 - },
218 - "loc": null,
219 - "range": {
220 - "start": 0,
221 - "end": 1
222 - }
223 - },
224 - "consequent": {
225 - "type": "BlockStatement",
226 - "body": [
227 - {
228 - "type": "BreakStatement",
229 - "label": {
230 - "type": "Identifier",
231 - "name": "foo",
232 - "typeAnnotation": null,
233 - "loc": null,
234 - "range": {
235 - "start": 0,
236 - "end": 1
237 - }
238 - },
239 - "loc": null,
240 - "range": {
241 - "start": 0,
242 - "end": 1
243 - }
244 - }
245 - ],
246 - "loc": null,
247 - "range": {
248 - "start": 0,
249 - "end": 1
250 - }
251 - },
252 - "alternate": null,
253 - "loc": null,
254 - "range": {
255 - "start": 0,
256 - "end": 1
257 - }
258 - },
259 - {
260 - "type": "ExpressionStatement",
261 - "expression": {
262 - "type": "AssignmentExpression",
263 - "operator": "=",
264 - "left": {
265 - "type": "Identifier",
266 - "name": "y",
267 - "typeAnnotation": null,
268 - "loc": null,
269 - "range": {
270 - "start": 0,
271 - "end": 1
272 - }
273 - },
274 - "right": {
275 - "type": "BinaryExpression",
276 - "left": {
277 - "type": "Identifier",
278 - "name": "x",
279 - "typeAnnotation": null,
280 - "loc": null,
281 - "range": {
282 - "start": 0,
283 - "end": 1
284 - }
285 - },
286 - "operator": "+",
287 - "right": {
288 - "type": "Identifier",
289 - "name": "y",
290 - "typeAnnotation": null,
291 - "loc": null,
292 - "range": {
293 - "start": 0,
294 - "end": 1
295 - }
296 - },
297 - "loc": null,
298 - "range": {
299 - "start": 0,
300 - "end": 1
301 - }
302 - },
303 - "loc": null,
304 - "range": {
305 - "start": 0,
306 - "end": 1
307 - }
308 - },
309 - "directive": null,
310 - "loc": null,
311 - "range": {
312 - "start": 0,
313 - "end": 1
314 - }
315 - },
316 - {
317 - "type": "ContinueStatement",
318 - "label": {
319 - "type": "Identifier",
320 - "name": "foo",
321 - "typeAnnotation": null,
322 - "loc": null,
323 - "range": {
324 - "start": 0,
325 - "end": 1
326 - }
327 - },
328 - "loc": null,
329 - "range": {
330 - "start": 0,
331 - "end": 1
332 - }
333 - }
334 - ],
335 - "loc": null,
336 - "range": {
337 - "start": 0,
338 - "end": 1
339 - }
340 - },
341 - "loc": null,
342 - "range": {
343 - "start": 0,
344 - "end": 1
345 - }
346 - },
347 - "loc": null,
348 - "range": {
349 - "start": 0,
350 - "end": 1
351 - }
352 - },
353 - {
354 - "type": "LabeledStatement",
355 - "label": {
356 - "type": "Identifier",
357 - "name": "bar",
358 - "typeAnnotation": null,
359 - "loc": null,
360 - "range": {
361 - "start": 0,
362 - "end": 1
363 - }
364 - },
365 - "body": {
366 - "type": "IfStatement",
367 - "test": {
368 - "type": "Identifier",
369 - "name": "props",
370 - "typeAnnotation": null,
371 - "loc": null,
372 - "range": {
373 - "start": 0,
374 - "end": 1
375 - }
376 - },
377 - "consequent": {
378 - "type": "BlockStatement",
379 - "body": [
380 - {
381 - "type": "BreakStatement",
382 - "label": {
383 - "type": "Identifier",
384 - "name": "bar",
385 - "typeAnnotation": null,
386 - "loc": null,
387 - "range": {
388 - "start": 0,
389 - "end": 1
390 - }
391 - },
392 - "loc": null,
393 - "range": {
394 - "start": 0,
395 - "end": 1
396 - }
397 - }
398 - ],
399 - "loc": null,
400 - "range": {
401 - "start": 0,
402 - "end": 1
403 - }
404 - },
405 - "alternate": null,
406 - "loc": null,
407 - "range": {
408 - "start": 0,
409 - "end": 1
410 - }
411 - },
412 - "loc": null,
413 - "range": {
414 - "start": 0,
415 - "end": 1
416 - }
417 - }
418 - ],
419 - "loc": null,
420 - "range": {
421 - "start": 0,
422 - "end": 1
423 - }
424 - },
425 - "generator": false,
426 - "async": false,
427 - "loc": null,
428 - "range": {
429 - "start": 0,
430 - "end": 1
431 - },
432 - "loc": null,
433 - "range": {
434 - "start": 0,
435 - "end": 1
436 - }
437 - }
438 - ],
439 - "sourceType": "module",
440 - "loc": null,
441 - "range": {
442 - "start": 0,
443 - "end": 1
444 - }
445 -}
446 -
22 Analysis:
23 Scope {
24 id: ScopeId(
compiler/forget/crates/forget_semantic_analysis/tests/snapshots/analysis_test__fixtures@simple-function.js.snap new
+223
@@ -0,0 +1,223 @@
1 +---
2 +source: crates/forget_semantic_analysis/tests/analysis_test.rs
3 +expression: "format!(\"Input:\\n{input}\\n\\nAnalysis:\\n{output}\")"
4 +input_file: crates/forget_semantic_analysis/tests/fixtures/simple-function.js
5 +---
6 +Input:
7 +function Component(a) {
8 + Math;
9 + let b = 0;
10 + const foo = function foo_(c) {
11 + let d = 1;
12 + return a + b + c + d;
13 + };
14 + return foo();
15 +}
16 +
17 +
18 +Analysis:
19 +Scope {
20 + id: ScopeId(
21 + 0,
22 + ),
23 + kind: Global,
24 + labels: {},
25 + declarations: {},
26 + references: [],
27 + children: [
28 + Scope {
29 + id: ScopeId(
30 + 1,
31 + ),
32 + kind: Module,
33 + labels: {},
34 + declarations: {
35 + "Component": Declaration {
36 + id: DeclarationId(
37 + 0,
38 + ),
39 + kind: FunctionDeclaration,
40 + scope: ScopeId(
41 + 1,
42 + ),
43 + },
44 + },
45 + references: [],
46 + children: [
47 + Scope {
48 + id: ScopeId(
49 + 2,
50 + ),
51 + kind: Function,
52 + labels: {},
53 + declarations: {
54 + "a": Declaration {
55 + id: DeclarationId(
56 + 1,
57 + ),
58 + kind: FunctionDeclaration,
59 + scope: ScopeId(
60 + 2,
61 + ),
62 + },
63 + "b": Declaration {
64 + id: DeclarationId(
65 + 2,
66 + ),
67 + kind: Let,
68 + scope: ScopeId(
69 + 2,
70 + ),
71 + },
72 + "foo": Declaration {
73 + id: DeclarationId(
74 + 3,
75 + ),
76 + kind: Const,
77 + scope: ScopeId(
78 + 2,
79 + ),
80 + },
81 + },
82 + references: [
83 + Reference {
84 + id: ReferenceId(
85 + 4,
86 + ),
87 + kind: Read,
88 + declaration: DeclarationId(
89 + 3,
90 + ),
91 + declaration (name): "foo",
92 + scope: ScopeId(
93 + 2,
94 + ),
95 + },
96 + ],
97 + children: [
98 + Scope {
99 + id: ScopeId(
100 + 3,
101 + ),
102 + kind: Function,
103 + labels: {},
104 + declarations: {
105 + "foo_": Declaration {
106 + id: DeclarationId(
107 + 4,
108 + ),
109 + kind: FunctionDeclaration,
110 + scope: ScopeId(
111 + 3,
112 + ),
113 + },
114 + },
115 + references: [],
116 + children: [
117 + Scope {
118 + id: ScopeId(
119 + 4,
120 + ),
121 + kind: Function,
122 + labels: {},
123 + declarations: {
124 + "c": Declaration {
125 + id: DeclarationId(
126 + 5,
127 + ),
128 + kind: FunctionDeclaration,
129 + scope: ScopeId(
130 + 4,
131 + ),
132 + },
133 + "d": Declaration {
134 + id: DeclarationId(
135 + 6,
136 + ),
137 + kind: Let,
138 + scope: ScopeId(
139 + 4,
140 + ),
141 + },
142 + },
143 + references: [
144 + Reference {
145 + id: ReferenceId(
146 + 0,
147 + ),
148 + kind: Read,
149 + declaration: DeclarationId(
150 + 1,
151 + ),
152 + declaration (name): "a",
153 + scope: ScopeId(
154 + 4,
155 + ),
156 + },
157 + Reference {
158 + id: ReferenceId(
159 + 1,
160 + ),
161 + kind: Read,
162 + declaration: DeclarationId(
163 + 2,
164 + ),
165 + declaration (name): "b",
166 + scope: ScopeId(
167 + 4,
168 + ),
169 + },
170 + Reference {
171 + id: ReferenceId(
172 + 2,
173 + ),
174 + kind: Read,
175 + declaration: DeclarationId(
176 + 5,
177 + ),
178 + declaration (name): "c",
179 + scope: ScopeId(
180 + 4,
181 + ),
182 + },
183 + Reference {
184 + id: ReferenceId(
185 + 3,
186 + ),
187 + kind: Read,
188 + declaration: DeclarationId(
189 + 6,
190 + ),
191 + declaration (name): "d",
192 + scope: ScopeId(
193 + 4,
194 + ),
195 + },
196 + ],
197 + children: [],
198 + },
199 + ],
200 + },
201 + ],
202 + },
203 + ],
204 + },
205 + ],
206 +}
207 +Diagnostic(
208 + DiagnosticData {
209 + message: "Undefined variable",
210 + span: Some(
211 + SourceSpan {
212 + offset: SourceOffset(
213 + 0,
214 + ),
215 + length: 1,
216 + },
217 + ),
218 + related_information: [],
219 + severity: InvalidSyntax,
220 + data: [],
221 + },
222 +)
223 +