[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
+