[rust][sema] Handle var/let/const redeclaration
JavaScript has ~sane~ fun rules around where variables can be redeclared or not, and which kinds of variables this applies to. Actually the rules are pretty straightforward: * `var` can be redeclared any number of times. * In strict mode, other declarations cannot be redeclared within the same scope. This implies that a `var` declaration cannot conflict with these other forms, which must take into account hoisting. So you can't have a `var a; let a` in the same scope, but you also can't have a `let a` at a scope and then a `var a` which will hoist to that same scope.
Joe Savona committed
Aug 16, 2023 at 12:26 UTC
bfb7d2bfbd5a8fe2906cf42a5a4a448406c741d7
16 files changed
+823
-71
compiler/forget/crates/forget_fixtures/tests/fixtures/use-memo.js
+5
-3
@@ -1,6 +1,8 @@
1
+import { useMemo } from "react";
2
+
3
function Component(x) {
2
- const x = useMemo(() => {
3
- return y;
4
+ const y = useMemo(() => {
5
+ return x;
6
});
5
- return x;
7
+ return y;
8
}
compiler/forget/crates/forget_fixtures/tests/fixtures_test.rs
+11
-1
@@ -21,7 +21,17 @@ fn fixtures() {
21
22
let mut output = String::new();
23
24
- let analysis = analyze(&ast);
24
+ let mut analysis = analyze(&ast);
25
+ let diagnostics = analysis.diagnostics();
26
+ if !diagnostics.is_empty() {
27
+ for diagnostic in diagnostics {
28
+ eprintln!(
29
+ "{:?}",
30
+ Report::new(diagnostic)
31
+ .with_source_code(NamedSource::new(path.to_string_lossy(), input.clone(),))
32
+ );
33
+ }
34
+ }
35
let environment = Environment::new(
36
Features {
37
validate_frozen_lambdas: true,
compiler/forget/crates/forget_fixtures/tests/snapshots/fixtures_test__fixtures@use-memo.js.snap
+8
-6
@@ -4,11 +4,13 @@ expression: "format!(\"Input:\\n{input}\\n\\nOutput:\\n{output}\")"
4
input_file: crates/forget_fixtures/tests/fixtures/use-memo.js
5
---
6
Input:
7
+import { useMemo } from "react";
8
+
9
function Component(x) {
8
- const x = useMemo(() => {
9
- return y;
10
+ const y = useMemo(() => {
11
+ return x;
12
});
11
- return x;
13
+ return y;
14
}
15
16
@@ -23,12 +25,12 @@ bb0 (block)
25
[2] Label block=bb1 fallthrough=bb6
26
bb1 (block)
27
predecessors: bb0
26
- [3] unknown $13 = LoadGlobal y
28
+ [3] unknown $13 = LoadLocal unknown x$10
29
[4] unknown $19 = StoreLocal Reassign unknown t$18 = unknown $13
30
[5] Goto bb6
31
bb6 (block)
32
predecessors: bb1
33
[6] unknown $14 = LoadLocal unknown t$18
32
- [7] unknown $16 = StoreLocal Const unknown x$15 = unknown $14
33
- [8] unknown $17 = LoadLocal unknown x$15
34
+ [7] unknown $16 = StoreLocal Const unknown y$15 = unknown $14
35
+ [8] unknown $17 = LoadLocal unknown y$15
36
[9] Return unknown $17
compiler/forget/crates/forget_semantic_analysis/src/analyzer.rs
+8
-19
@@ -157,7 +157,7 @@ impl Analyzer {
157
Analyzer::visit_declaration_pattern(
158
visitor,
159
param,
160
- Some(DeclarationKind::FunctionDeclaration),
160
+ Some(DeclarationKind::Function),
161
);
162
}
163
@@ -204,22 +204,9 @@ impl Analyzer {
204
decl_kind: Option<DeclarationKind>,
205
) {
206
if let Some(decl_kind) = decl_kind {
207
- // Declaring a "new" variable, report an error if this is a duplicate
208
- // definition. In either case, we create a new declaration. Ie we
209
- // act as if shadowing is allowed in the language
210
- let previous_declaration = self.manager.lookup_declaration(self.current, &ast.name);
211
- if let Some(previous_declaration) = previous_declaration {
212
- if previous_declaration.scope == self.current {
213
- // duplicate definition in the same scope
214
- self.manager.diagnostics.push(Diagnostic::invalid_syntax(
215
- "Duplicate declaration",
216
- ast.range,
217
- ));
218
- }
219
- }
220
- let id = self
221
- .manager
222
- .add_declaration(self.current, ast.name.clone(), decl_kind);
207
+ let id =
208
+ self.manager
209
+ .add_declaration(self.current, ast.name.clone(), decl_kind, ast.range);
210
self.manager
211
.node_declarations
212
.insert(AstNode::from(ast), id);
@@ -350,7 +337,8 @@ impl Visitor for Analyzer {
337
let declaration = self.manager.add_declaration(
338
self.current,
339
id.name.clone(),
353
- DeclarationKind::FunctionDeclaration,
340
+ DeclarationKind::Function,
341
+ id.range,
342
);
343
self.manager
344
.node_declarations
@@ -366,7 +354,8 @@ impl Visitor for Analyzer {
354
let declaration = self.manager.add_declaration(
355
self.current,
356
id.name.clone(),
369
- DeclarationKind::FunctionDeclaration,
357
+ DeclarationKind::Function,
358
+ id.range,
359
);
360
self.manager
361
.node_declarations
compiler/forget/crates/forget_semantic_analysis/src/scope_manager.rs
+87
-23
@@ -1,6 +1,6 @@
1
use forget_diagnostics::Diagnostic;
2
use forget_estree::{
3
- BreakStatement, ContinueStatement, ESTreeNode, LabeledStatement, SourceType,
3
+ BreakStatement, ContinueStatement, ESTreeNode, LabeledStatement, SourceRange, SourceType,
4
VariableDeclarationKind,
5
};
6
use forget_utils::PointerAddress;
@@ -174,20 +174,6 @@ impl ScopeManager {
174
})
175
}
176
177
- pub fn lookup_declaration(&self, scope: ScopeId, name: &str) -> Option<&Declaration> {
178
- let mut current = &self.scopes[scope.0];
179
- loop {
180
- if let Some(id) = current.declarations.get(name) {
181
- return Some(&self.declarations[id.0]);
182
- }
183
- if let Some(parent) = current.parent {
184
- current = &self.scopes[parent.0];
185
- } else {
186
- return None;
187
- }
188
- }
189
- }
190
-
177
pub fn lookup_reference(
178
&self,
179
scope: ScopeId,
@@ -271,20 +257,86 @@ impl ScopeManager {
257
258
pub(crate) fn add_declaration(
259
&mut self,
274
- scope: ScopeId,
260
+ scope_id: ScopeId,
261
name: String,
262
kind: DeclarationKind,
263
+ range: Option<SourceRange>,
264
) -> DeclarationId {
278
- let hoisted_scope = self.get_scope_for_declaration(scope, kind);
265
+ let scope = self.scope(scope_id);
266
+ // Determine the scope to which this declaration should be hoisted. This mainly applies to var declarations
267
+ let hoisted_scope_id = self.get_scope_for_declaration(scope_id, kind);
268
+
269
+ // Check for redeclaration. The rules are roughly:
270
+ // * `var` can be redeclared any number of times in a given scope. These redeclarations have no effect,
271
+ // subsequent declarations are equivalent to just reassigning a value to the original declaration.
272
+ // ie `var a = 1; var a = 2;` is equivalent to `var a; a = 1; a = 2`.
273
+ // * Other forms (in strict mode) may not be redeclared in a given scope.
274
+ // * This implies that `var` cannot conflict with other types of declarations, either in the scope
275
+ // at which they are declared or the scope to which the var will hoist:
276
+ // * `function() { {let a; var a;} }` conflicts at the declaration scope, even though the var will hoise above.
277
+ // * `function() { let a; { var a; } }` conflicts bc the var hoists to the scope w a conflicting let.
278
+ match kind {
279
+ DeclarationKind::Var => {
280
+ if let Some(declaration) = scope.declarations.get(&name) {
281
+ let declaration = self.declaration(*declaration);
282
+ if is_block_scoped_declaration(declaration.kind) {
283
+ // Var cannot be declared in the same scope as let/const/class/import/etc
284
+ self.diagnostics
285
+ .push(Diagnostic::invalid_syntax("Duplicate declaration", range));
286
+ }
287
+ } else if hoisted_scope_id != scope_id {
288
+ if let Some(declaration) = self.scope(hoisted_scope_id).declarations.get(&name)
289
+ {
290
+ let declaration = self.declaration(*declaration);
291
+ if is_block_scoped_declaration(declaration.kind) {
292
+ // Var cannot *hoist* to the same scope as let/const/class/import/etc
293
+ self.diagnostics
294
+ .push(Diagnostic::invalid_syntax("Duplicate declaration", range));
295
+ }
296
+ }
297
+ }
298
+ // Redeclaration of `var` in a given scope has no effect, subsequent declarations
299
+ // are equivalent to re-declarations
300
+ // ie `var a = 1; var a = 2;` is equivalent to `var a; a = 1; a = 2`.
301
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var#redeclarations
302
+ if let Some(declaration) = self.scope(hoisted_scope_id).declarations.get(&name) {
303
+ let declaration = self.declaration(*declaration);
304
+ if declaration.kind == DeclarationKind::Var {
305
+ return declaration.id;
306
+ }
307
+ }
308
+ }
309
+ DeclarationKind::CatchClause
310
+ | DeclarationKind::Let
311
+ | DeclarationKind::Const
312
+ | DeclarationKind::Import
313
+ | DeclarationKind::Class
314
+ | DeclarationKind::Function => {
315
+ // When duplicate declarations occur we report an error and then resolve references to the
316
+ // first declaration. It doesn't really matter which declaration we refer to, because
317
+ // semantic results are invalid if there are errors. The main consideration is that we do
318
+ // not want to report a "cannot find declaration for `x`" reference error just because there
319
+ // were duplicate declarations of `x`.
320
+ if let Some(_declaration) = scope.declarations.get(&name) {
321
+ self.diagnostics
322
+ .push(Diagnostic::invalid_syntax("Duplicate declaration", range));
323
+ }
324
+ }
325
+ }
326
327
+ // Always create a new declaration and id...
328
let id = DeclarationId(self.declarations.len());
329
self.declarations.push(Declaration {
330
id,
331
kind,
332
name: name.clone(),
285
- scope: hoisted_scope,
333
+ scope: hoisted_scope_id,
334
});
287
- self.scopes[hoisted_scope.0].declarations.insert(name, id);
335
+ // ...but only save the first declaration for a given name in each scope
336
+ self.scopes[hoisted_scope_id.0]
337
+ .declarations
338
+ .entry(name)
339
+ .or_insert(id);
340
id
341
}
342
@@ -294,8 +346,8 @@ impl ScopeManager {
346
| DeclarationKind::Import
347
| DeclarationKind::Const
348
| DeclarationKind::CatchClause
297
- | DeclarationKind::For => scope,
298
- DeclarationKind::Var | DeclarationKind::FunctionDeclaration => {
349
+ | DeclarationKind::Class => scope,
350
+ DeclarationKind::Var | DeclarationKind::Function => {
351
let mut current = scope;
352
loop {
353
let scope = self.scope(current);
@@ -342,6 +394,18 @@ impl ScopeManager {
394
}
395
}
396
397
+fn is_block_scoped_declaration(kind: DeclarationKind) -> bool {
398
+ match kind {
399
+ DeclarationKind::Let
400
+ | DeclarationKind::Const
401
+ | DeclarationKind::Import
402
+ | DeclarationKind::Class
403
+ | DeclarationKind::Function
404
+ | DeclarationKind::CatchClause => true,
405
+ DeclarationKind::Var => false,
406
+ }
407
+}
408
+
409
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone)]
410
pub struct ScopeId(usize);
411
@@ -393,11 +457,11 @@ pub struct Label {
457
458
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone)]
459
pub enum DeclarationKind {
460
+ Class,
461
Const,
462
Var,
463
Let,
399
- FunctionDeclaration,
400
- For,
464
+ Function,
465
CatchClause,
466
Import,
467
}
compiler/forget/crates/forget_semantic_analysis/tests/fixtures/block-item-duplication.js
new
+25
@@ -0,0 +1,25 @@
1
+function Component() {
2
+ let a = 1;
3
+ let a = 2; // error
4
+
5
+ const b = 3;
6
+ const b = 4; // error
7
+
8
+ function foo() {}
9
+ function foo() {} // error
10
+
11
+ try {
12
+ } catch (c) {
13
+ let c = true; // error
14
+ const c = true; // error
15
+ function c() {} // error
16
+ // class c {} // error
17
+ }
18
+}
19
+
20
+function Component() {
21
+ // error
22
+}
23
+
24
+const x = true;
25
+const x = false; // error
compiler/forget/crates/forget_semantic_analysis/tests/fixtures/var-duplication.js
new
+29
@@ -0,0 +1,29 @@
1
+function Component() {
2
+ let a;
3
+ {
4
+ var a; // error, conflicts when hoisted
5
+ }
6
+
7
+ const b = 1;
8
+ {
9
+ var b; // error, conflicts
10
+ }
11
+
12
+ {
13
+ let c;
14
+ var c; // error, conflicts
15
+ }
16
+
17
+ {
18
+ const d = 2;
19
+ var d; // error, conflicts
20
+ }
21
+
22
+ // there should be one instance of `e`:
23
+ var e = 3;
24
+ console.log(e); // 3
25
+ var e = 4;
26
+ console.log(e); // 4
27
+ var e;
28
+ console.log(e); // 4
29
+}
compiler/forget/crates/forget_semantic_analysis/tests/snapshots/analysis_test__fixtures@block-item-duplication.js.snap
new
+308
@@ -0,0 +1,308 @@
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/block-item-duplication.js
5
+---
6
+Input:
7
+function Component() {
8
+ let a = 1;
9
+ let a = 2; // error
10
+
11
+ const b = 3;
12
+ const b = 4; // error
13
+
14
+ function foo() {}
15
+ function foo() {} // error
16
+
17
+ try {
18
+ } catch (c) {
19
+ let c = true; // error
20
+ const c = true; // error
21
+ function c() {} // error
22
+ // class c {} // error
23
+ }
24
+}
25
+
26
+function Component() {
27
+ // error
28
+}
29
+
30
+const x = true;
31
+const x = false; // error
32
+
33
+
34
+Analysis:
35
+Scope {
36
+ id: ScopeId(
37
+ 0,
38
+ ),
39
+ kind: Module,
40
+ declarations: {
41
+ "Component": Declaration {
42
+ id: DeclarationId(
43
+ 0,
44
+ ),
45
+ kind: Function,
46
+ scope: ScopeId(
47
+ 0,
48
+ ),
49
+ },
50
+ "x": Declaration {
51
+ id: DeclarationId(
52
+ 12,
53
+ ),
54
+ kind: Const,
55
+ scope: ScopeId(
56
+ 0,
57
+ ),
58
+ },
59
+ },
60
+ references: [],
61
+ children: [
62
+ Scope {
63
+ id: ScopeId(
64
+ 1,
65
+ ),
66
+ kind: Function,
67
+ declarations: {
68
+ "a": Declaration {
69
+ id: DeclarationId(
70
+ 1,
71
+ ),
72
+ kind: Let,
73
+ scope: ScopeId(
74
+ 1,
75
+ ),
76
+ },
77
+ "b": Declaration {
78
+ id: DeclarationId(
79
+ 3,
80
+ ),
81
+ kind: Const,
82
+ scope: ScopeId(
83
+ 1,
84
+ ),
85
+ },
86
+ "foo": Declaration {
87
+ id: DeclarationId(
88
+ 5,
89
+ ),
90
+ kind: Function,
91
+ scope: ScopeId(
92
+ 1,
93
+ ),
94
+ },
95
+ "c": Declaration {
96
+ id: DeclarationId(
97
+ 10,
98
+ ),
99
+ kind: Function,
100
+ scope: ScopeId(
101
+ 1,
102
+ ),
103
+ },
104
+ },
105
+ references: [],
106
+ children: [
107
+ Scope {
108
+ id: ScopeId(
109
+ 2,
110
+ ),
111
+ kind: Function,
112
+ declarations: {},
113
+ references: [],
114
+ children: [],
115
+ },
116
+ Scope {
117
+ id: ScopeId(
118
+ 3,
119
+ ),
120
+ kind: Function,
121
+ declarations: {},
122
+ references: [],
123
+ children: [],
124
+ },
125
+ Scope {
126
+ id: ScopeId(
127
+ 4,
128
+ ),
129
+ kind: Block,
130
+ declarations: {},
131
+ references: [],
132
+ children: [],
133
+ },
134
+ Scope {
135
+ id: ScopeId(
136
+ 5,
137
+ ),
138
+ kind: CatchClause,
139
+ declarations: {
140
+ "c": Declaration {
141
+ id: DeclarationId(
142
+ 7,
143
+ ),
144
+ kind: CatchClause,
145
+ scope: ScopeId(
146
+ 5,
147
+ ),
148
+ },
149
+ },
150
+ references: [],
151
+ children: [
152
+ Scope {
153
+ id: ScopeId(
154
+ 6,
155
+ ),
156
+ kind: Block,
157
+ declarations: {
158
+ "c": Declaration {
159
+ id: DeclarationId(
160
+ 8,
161
+ ),
162
+ kind: Let,
163
+ scope: ScopeId(
164
+ 6,
165
+ ),
166
+ },
167
+ },
168
+ references: [],
169
+ children: [
170
+ Scope {
171
+ id: ScopeId(
172
+ 7,
173
+ ),
174
+ kind: Function,
175
+ declarations: {},
176
+ references: [],
177
+ children: [],
178
+ },
179
+ ],
180
+ },
181
+ ],
182
+ },
183
+ ],
184
+ },
185
+ Scope {
186
+ id: ScopeId(
187
+ 8,
188
+ ),
189
+ kind: Function,
190
+ declarations: {},
191
+ references: [],
192
+ children: [],
193
+ },
194
+ ],
195
+}
196
+Diagnostic(
197
+ DiagnosticData {
198
+ message: "Duplicate declaration",
199
+ span: Some(
200
+ SourceSpan {
201
+ offset: SourceOffset(
202
+ 42,
203
+ ),
204
+ length: 1,
205
+ },
206
+ ),
207
+ related_information: [],
208
+ severity: InvalidSyntax,
209
+ data: [],
210
+ },
211
+)
212
+Diagnostic(
213
+ DiagnosticData {
214
+ message: "Duplicate declaration",
215
+ span: Some(
216
+ SourceSpan {
217
+ offset: SourceOffset(
218
+ 82,
219
+ ),
220
+ length: 1,
221
+ },
222
+ ),
223
+ related_information: [],
224
+ severity: InvalidSyntax,
225
+ data: [],
226
+ },
227
+)
228
+Diagnostic(
229
+ DiagnosticData {
230
+ message: "Duplicate declaration",
231
+ span: Some(
232
+ SourceSpan {
233
+ offset: SourceOffset(
234
+ 130,
235
+ ),
236
+ length: 3,
237
+ },
238
+ ),
239
+ related_information: [],
240
+ severity: InvalidSyntax,
241
+ data: [],
242
+ },
243
+)
244
+Diagnostic(
245
+ DiagnosticData {
246
+ message: "Duplicate declaration",
247
+ span: Some(
248
+ SourceSpan {
249
+ offset: SourceOffset(
250
+ 210,
251
+ ),
252
+ length: 1,
253
+ },
254
+ ),
255
+ related_information: [],
256
+ severity: InvalidSyntax,
257
+ data: [],
258
+ },
259
+)
260
+Diagnostic(
261
+ DiagnosticData {
262
+ message: "Duplicate declaration",
263
+ span: Some(
264
+ SourceSpan {
265
+ offset: SourceOffset(
266
+ 242,
267
+ ),
268
+ length: 1,
269
+ },
270
+ ),
271
+ related_information: [],
272
+ severity: InvalidSyntax,
273
+ data: [],
274
+ },
275
+)
276
+Diagnostic(
277
+ DiagnosticData {
278
+ message: "Duplicate declaration",
279
+ span: Some(
280
+ SourceSpan {
281
+ offset: SourceOffset(
282
+ 301,
283
+ ),
284
+ length: 9,
285
+ },
286
+ ),
287
+ related_information: [],
288
+ severity: InvalidSyntax,
289
+ data: [],
290
+ },
291
+)
292
+Diagnostic(
293
+ DiagnosticData {
294
+ message: "Duplicate declaration",
295
+ span: Some(
296
+ SourceSpan {
297
+ offset: SourceOffset(
298
+ 351,
299
+ ),
300
+ length: 1,
301
+ },
302
+ ),
303
+ related_information: [],
304
+ severity: InvalidSyntax,
305
+ data: [],
306
+ },
307
+)
308
+
compiler/forget/crates/forget_semantic_analysis/tests/snapshots/analysis_test__fixtures@function-hoisting.js.snap
+4
-4
@@ -39,7 +39,7 @@ Scope {
39
id: DeclarationId(
40
0,
41
),
42
- kind: FunctionDeclaration,
42
+ kind: Function,
43
scope: ScopeId(
44
0,
45
),
@@ -57,7 +57,7 @@ Scope {
57
id: DeclarationId(
58
1,
59
),
60
- kind: FunctionDeclaration,
60
+ kind: Function,
61
scope: ScopeId(
62
1,
63
),
@@ -66,7 +66,7 @@ Scope {
66
id: DeclarationId(
67
2,
68
),
69
- kind: FunctionDeclaration,
69
+ kind: Function,
70
scope: ScopeId(
71
1,
72
),
@@ -191,7 +191,7 @@ Scope {
191
id: DeclarationId(
192
4,
193
),
194
- kind: FunctionDeclaration,
194
+ kind: Function,
195
scope: ScopeId(
196
9,
197
),
compiler/forget/crates/forget_semantic_analysis/tests/snapshots/analysis_test__fixtures@globals-and-imports.js.snap
+2
-2
@@ -61,7 +61,7 @@ Scope {
61
id: DeclarationId(
62
3,
63
),
64
- kind: FunctionDeclaration,
64
+ kind: Function,
65
scope: ScopeId(
66
0,
67
),
@@ -79,7 +79,7 @@ Scope {
79
id: DeclarationId(
80
4,
81
),
82
- kind: FunctionDeclaration,
82
+ kind: Function,
83
scope: ScopeId(
84
1,
85
),
compiler/forget/crates/forget_semantic_analysis/tests/snapshots/analysis_test__fixtures@labels.js.snap
+2
-2
@@ -30,7 +30,7 @@ Scope {
30
id: DeclarationId(
31
0,
32
),
33
- kind: FunctionDeclaration,
33
+ kind: Function,
34
scope: ScopeId(
35
0,
36
),
@@ -48,7 +48,7 @@ Scope {
48
id: DeclarationId(
49
1,
50
),
51
- kind: FunctionDeclaration,
51
+ kind: Function,
52
scope: ScopeId(
53
1,
54
),
compiler/forget/crates/forget_semantic_analysis/tests/snapshots/analysis_test__fixtures@let-const-hoisting.js.snap
+2
-2
@@ -25,7 +25,7 @@ Scope {
25
id: DeclarationId(
26
0,
27
),
28
- kind: FunctionDeclaration,
28
+ kind: Function,
29
scope: ScopeId(
30
0,
31
),
@@ -43,7 +43,7 @@ Scope {
43
id: DeclarationId(
44
1,
45
),
46
- kind: FunctionDeclaration,
46
+ kind: Function,
47
scope: ScopeId(
48
1,
49
),
compiler/forget/crates/forget_semantic_analysis/tests/snapshots/analysis_test__fixtures@simple-function.js.snap
+4
-4
@@ -26,7 +26,7 @@ Scope {
26
id: DeclarationId(
27
0,
28
),
29
- kind: FunctionDeclaration,
29
+ kind: Function,
30
scope: ScopeId(
31
0,
32
),
@@ -44,7 +44,7 @@ Scope {
44
id: DeclarationId(
45
1,
46
),
47
- kind: FunctionDeclaration,
47
+ kind: Function,
48
scope: ScopeId(
49
1,
50
),
@@ -94,7 +94,7 @@ Scope {
94
id: DeclarationId(
95
4,
96
),
97
- kind: FunctionDeclaration,
97
+ kind: Function,
98
scope: ScopeId(
99
2,
100
),
@@ -112,7 +112,7 @@ Scope {
112
id: DeclarationId(
113
5,
114
),
115
- kind: FunctionDeclaration,
115
+ kind: Function,
116
scope: ScopeId(
117
3,
118
),
compiler/forget/crates/forget_semantic_analysis/tests/snapshots/analysis_test__fixtures@tdz.js.snap
+2
-2
@@ -31,7 +31,7 @@ Scope {
31
id: DeclarationId(
32
0,
33
),
34
- kind: FunctionDeclaration,
34
+ kind: Function,
35
scope: ScopeId(
36
0,
37
),
@@ -49,7 +49,7 @@ Scope {
49
id: DeclarationId(
50
1,
51
),
52
- kind: FunctionDeclaration,
52
+ kind: Function,
53
scope: ScopeId(
54
1,
55
),
compiler/forget/crates/forget_semantic_analysis/tests/snapshots/analysis_test__fixtures@var-duplication.js.snap
new
+323
@@ -0,0 +1,323 @@
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/var-duplication.js
5
+---
6
+Input:
7
+function Component() {
8
+ let a;
9
+ {
10
+ var a; // error, conflicts when hoisted
11
+ }
12
+
13
+ const b = 1;
14
+ {
15
+ var b; // error, conflicts
16
+ }
17
+
18
+ {
19
+ let c;
20
+ var c; // error, conflicts
21
+ }
22
+
23
+ {
24
+ const d = 2;
25
+ var d; // error, conflicts
26
+ }
27
+
28
+ // there should be one instance of `e`:
29
+ var e = 3;
30
+ console.log(e); // 3
31
+ var e = 4;
32
+ console.log(e); // 4
33
+ var e;
34
+ console.log(e); // 4
35
+}
36
+
37
+
38
+Analysis:
39
+Scope {
40
+ id: ScopeId(
41
+ 0,
42
+ ),
43
+ kind: Module,
44
+ declarations: {
45
+ "Component": Declaration {
46
+ id: DeclarationId(
47
+ 0,
48
+ ),
49
+ kind: Function,
50
+ scope: ScopeId(
51
+ 0,
52
+ ),
53
+ },
54
+ },
55
+ references: [],
56
+ children: [
57
+ Scope {
58
+ id: ScopeId(
59
+ 1,
60
+ ),
61
+ kind: Function,
62
+ declarations: {
63
+ "a": Declaration {
64
+ id: DeclarationId(
65
+ 1,
66
+ ),
67
+ kind: Let,
68
+ scope: ScopeId(
69
+ 1,
70
+ ),
71
+ },
72
+ "b": Declaration {
73
+ id: DeclarationId(
74
+ 3,
75
+ ),
76
+ kind: Const,
77
+ scope: ScopeId(
78
+ 1,
79
+ ),
80
+ },
81
+ "c": Declaration {
82
+ id: DeclarationId(
83
+ 6,
84
+ ),
85
+ kind: Var,
86
+ scope: ScopeId(
87
+ 1,
88
+ ),
89
+ },
90
+ "d": Declaration {
91
+ id: DeclarationId(
92
+ 8,
93
+ ),
94
+ kind: Var,
95
+ scope: ScopeId(
96
+ 1,
97
+ ),
98
+ },
99
+ "e": Declaration {
100
+ id: DeclarationId(
101
+ 9,
102
+ ),
103
+ kind: Var,
104
+ scope: ScopeId(
105
+ 1,
106
+ ),
107
+ },
108
+ },
109
+ references: [
110
+ Reference {
111
+ id: ReferenceId(
112
+ 0,
113
+ ),
114
+ kind: Read,
115
+ declaration: DeclarationId(
116
+ 9,
117
+ ),
118
+ declaration (name): "e",
119
+ scope: ScopeId(
120
+ 1,
121
+ ),
122
+ },
123
+ Reference {
124
+ id: ReferenceId(
125
+ 1,
126
+ ),
127
+ kind: Read,
128
+ declaration: DeclarationId(
129
+ 9,
130
+ ),
131
+ declaration (name): "e",
132
+ scope: ScopeId(
133
+ 1,
134
+ ),
135
+ },
136
+ Reference {
137
+ id: ReferenceId(
138
+ 2,
139
+ ),
140
+ kind: Read,
141
+ declaration: DeclarationId(
142
+ 9,
143
+ ),
144
+ declaration (name): "e",
145
+ scope: ScopeId(
146
+ 1,
147
+ ),
148
+ },
149
+ ],
150
+ children: [
151
+ Scope {
152
+ id: ScopeId(
153
+ 2,
154
+ ),
155
+ kind: Block,
156
+ declarations: {},
157
+ references: [],
158
+ children: [],
159
+ },
160
+ Scope {
161
+ id: ScopeId(
162
+ 3,
163
+ ),
164
+ kind: Block,
165
+ declarations: {},
166
+ references: [],
167
+ children: [],
168
+ },
169
+ Scope {
170
+ id: ScopeId(
171
+ 4,
172
+ ),
173
+ kind: Block,
174
+ declarations: {
175
+ "c": Declaration {
176
+ id: DeclarationId(
177
+ 5,
178
+ ),
179
+ kind: Let,
180
+ scope: ScopeId(
181
+ 4,
182
+ ),
183
+ },
184
+ },
185
+ references: [],
186
+ children: [],
187
+ },
188
+ Scope {
189
+ id: ScopeId(
190
+ 5,
191
+ ),
192
+ kind: Block,
193
+ declarations: {
194
+ "d": Declaration {
195
+ id: DeclarationId(
196
+ 7,
197
+ ),
198
+ kind: Const,
199
+ scope: ScopeId(
200
+ 5,
201
+ ),
202
+ },
203
+ },
204
+ references: [],
205
+ children: [],
206
+ },
207
+ ],
208
+ },
209
+ ],
210
+}
211
+Diagnostic(
212
+ DiagnosticData {
213
+ message: "Duplicate declaration",
214
+ span: Some(
215
+ SourceSpan {
216
+ offset: SourceOffset(
217
+ 44,
218
+ ),
219
+ length: 1,
220
+ },
221
+ ),
222
+ related_information: [],
223
+ severity: InvalidSyntax,
224
+ data: [],
225
+ },
226
+)
227
+Diagnostic(
228
+ DiagnosticData {
229
+ message: "Duplicate declaration",
230
+ span: Some(
231
+ SourceSpan {
232
+ offset: SourceOffset(
233
+ 112,
234
+ ),
235
+ length: 1,
236
+ },
237
+ ),
238
+ related_information: [],
239
+ severity: InvalidSyntax,
240
+ data: [],
241
+ },
242
+)
243
+Diagnostic(
244
+ DiagnosticData {
245
+ message: "Duplicate declaration",
246
+ span: Some(
247
+ SourceSpan {
248
+ offset: SourceOffset(
249
+ 163,
250
+ ),
251
+ length: 1,
252
+ },
253
+ ),
254
+ related_information: [],
255
+ severity: InvalidSyntax,
256
+ data: [],
257
+ },
258
+)
259
+Diagnostic(
260
+ DiagnosticData {
261
+ message: "Duplicate declaration",
262
+ span: Some(
263
+ SourceSpan {
264
+ offset: SourceOffset(
265
+ 220,
266
+ ),
267
+ length: 1,
268
+ },
269
+ ),
270
+ related_information: [],
271
+ severity: InvalidSyntax,
272
+ data: [],
273
+ },
274
+)
275
+Diagnostic(
276
+ DiagnosticData {
277
+ message: "Undefined variable",
278
+ span: Some(
279
+ SourceSpan {
280
+ offset: SourceOffset(
281
+ 305,
282
+ ),
283
+ length: 7,
284
+ },
285
+ ),
286
+ related_information: [],
287
+ severity: InvalidSyntax,
288
+ data: [],
289
+ },
290
+)
291
+Diagnostic(
292
+ DiagnosticData {
293
+ message: "Undefined variable",
294
+ span: Some(
295
+ SourceSpan {
296
+ offset: SourceOffset(
297
+ 341,
298
+ ),
299
+ length: 7,
300
+ },
301
+ ),
302
+ related_information: [],
303
+ severity: InvalidSyntax,
304
+ data: [],
305
+ },
306
+)
307
+Diagnostic(
308
+ DiagnosticData {
309
+ message: "Undefined variable",
310
+ span: Some(
311
+ SourceSpan {
312
+ offset: SourceOffset(
313
+ 373,
314
+ ),
315
+ length: 7,
316
+ },
317
+ ),
318
+ related_information: [],
319
+ severity: InvalidSyntax,
320
+ data: [],
321
+ },
322
+)
323
+
compiler/forget/crates/forget_semantic_analysis/tests/snapshots/analysis_test__fixtures@var-hoisting.js.snap
+3
-3
@@ -36,7 +36,7 @@ Scope {
36
id: DeclarationId(
37
0,
38
),
39
- kind: FunctionDeclaration,
39
+ kind: Function,
40
scope: ScopeId(
41
0,
42
),
@@ -63,7 +63,7 @@ Scope {
63
id: DeclarationId(
64
1,
65
),
66
- kind: FunctionDeclaration,
66
+ kind: Function,
67
scope: ScopeId(
68
1,
69
),
@@ -72,7 +72,7 @@ Scope {
72
id: DeclarationId(
73
2,
74
),
75
- kind: FunctionDeclaration,
75
+ kind: Function,
76
scope: ScopeId(
77
1,
78
),