@samitouri / QOS-React-2 / commits / bfb7d2bfbd

[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 ),