mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[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.
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import { useMemo } from "react";
|
||||
|
||||
function Component(x) {
|
||||
const x = useMemo(() => {
|
||||
return y;
|
||||
const y = useMemo(() => {
|
||||
return x;
|
||||
});
|
||||
return x;
|
||||
return y;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,17 @@ fn fixtures() {
|
||||
|
||||
let mut output = String::new();
|
||||
|
||||
let analysis = analyze(&ast);
|
||||
let mut analysis = analyze(&ast);
|
||||
let diagnostics = analysis.diagnostics();
|
||||
if !diagnostics.is_empty() {
|
||||
for diagnostic in diagnostics {
|
||||
eprintln!(
|
||||
"{:?}",
|
||||
Report::new(diagnostic)
|
||||
.with_source_code(NamedSource::new(path.to_string_lossy(), input.clone(),))
|
||||
);
|
||||
}
|
||||
}
|
||||
let environment = Environment::new(
|
||||
Features {
|
||||
validate_frozen_lambdas: true,
|
||||
|
||||
+8
-6
@@ -4,11 +4,13 @@ expression: "format!(\"Input:\\n{input}\\n\\nOutput:\\n{output}\")"
|
||||
input_file: crates/forget_fixtures/tests/fixtures/use-memo.js
|
||||
---
|
||||
Input:
|
||||
import { useMemo } from "react";
|
||||
|
||||
function Component(x) {
|
||||
const x = useMemo(() => {
|
||||
return y;
|
||||
const y = useMemo(() => {
|
||||
return x;
|
||||
});
|
||||
return x;
|
||||
return y;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,12 +25,12 @@ bb0 (block)
|
||||
[2] Label block=bb1 fallthrough=bb6
|
||||
bb1 (block)
|
||||
predecessors: bb0
|
||||
[3] unknown $13 = LoadGlobal y
|
||||
[3] unknown $13 = LoadLocal unknown x$10
|
||||
[4] unknown $19 = StoreLocal Reassign unknown t$18 = unknown $13
|
||||
[5] Goto bb6
|
||||
bb6 (block)
|
||||
predecessors: bb1
|
||||
[6] unknown $14 = LoadLocal unknown t$18
|
||||
[7] unknown $16 = StoreLocal Const unknown x$15 = unknown $14
|
||||
[8] unknown $17 = LoadLocal unknown x$15
|
||||
[7] unknown $16 = StoreLocal Const unknown y$15 = unknown $14
|
||||
[8] unknown $17 = LoadLocal unknown y$15
|
||||
[9] Return unknown $17
|
||||
|
||||
@@ -157,7 +157,7 @@ impl Analyzer {
|
||||
Analyzer::visit_declaration_pattern(
|
||||
visitor,
|
||||
param,
|
||||
Some(DeclarationKind::FunctionDeclaration),
|
||||
Some(DeclarationKind::Function),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -204,22 +204,9 @@ impl Analyzer {
|
||||
decl_kind: Option<DeclarationKind>,
|
||||
) {
|
||||
if let Some(decl_kind) = decl_kind {
|
||||
// Declaring a "new" variable, report an error if this is a duplicate
|
||||
// definition. In either case, we create a new declaration. Ie we
|
||||
// act as if shadowing is allowed in the language
|
||||
let previous_declaration = self.manager.lookup_declaration(self.current, &ast.name);
|
||||
if let Some(previous_declaration) = previous_declaration {
|
||||
if previous_declaration.scope == self.current {
|
||||
// duplicate definition in the same scope
|
||||
self.manager.diagnostics.push(Diagnostic::invalid_syntax(
|
||||
"Duplicate declaration",
|
||||
ast.range,
|
||||
));
|
||||
}
|
||||
}
|
||||
let id = self
|
||||
.manager
|
||||
.add_declaration(self.current, ast.name.clone(), decl_kind);
|
||||
let id =
|
||||
self.manager
|
||||
.add_declaration(self.current, ast.name.clone(), decl_kind, ast.range);
|
||||
self.manager
|
||||
.node_declarations
|
||||
.insert(AstNode::from(ast), id);
|
||||
@@ -350,7 +337,8 @@ impl Visitor for Analyzer {
|
||||
let declaration = self.manager.add_declaration(
|
||||
self.current,
|
||||
id.name.clone(),
|
||||
DeclarationKind::FunctionDeclaration,
|
||||
DeclarationKind::Function,
|
||||
id.range,
|
||||
);
|
||||
self.manager
|
||||
.node_declarations
|
||||
@@ -366,7 +354,8 @@ impl Visitor for Analyzer {
|
||||
let declaration = self.manager.add_declaration(
|
||||
self.current,
|
||||
id.name.clone(),
|
||||
DeclarationKind::FunctionDeclaration,
|
||||
DeclarationKind::Function,
|
||||
id.range,
|
||||
);
|
||||
self.manager
|
||||
.node_declarations
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use forget_diagnostics::Diagnostic;
|
||||
use forget_estree::{
|
||||
BreakStatement, ContinueStatement, ESTreeNode, LabeledStatement, SourceType,
|
||||
BreakStatement, ContinueStatement, ESTreeNode, LabeledStatement, SourceRange, SourceType,
|
||||
VariableDeclarationKind,
|
||||
};
|
||||
use forget_utils::PointerAddress;
|
||||
@@ -174,20 +174,6 @@ impl ScopeManager {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn lookup_declaration(&self, scope: ScopeId, name: &str) -> Option<&Declaration> {
|
||||
let mut current = &self.scopes[scope.0];
|
||||
loop {
|
||||
if let Some(id) = current.declarations.get(name) {
|
||||
return Some(&self.declarations[id.0]);
|
||||
}
|
||||
if let Some(parent) = current.parent {
|
||||
current = &self.scopes[parent.0];
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup_reference(
|
||||
&self,
|
||||
scope: ScopeId,
|
||||
@@ -271,20 +257,86 @@ impl ScopeManager {
|
||||
|
||||
pub(crate) fn add_declaration(
|
||||
&mut self,
|
||||
scope: ScopeId,
|
||||
scope_id: ScopeId,
|
||||
name: String,
|
||||
kind: DeclarationKind,
|
||||
range: Option<SourceRange>,
|
||||
) -> DeclarationId {
|
||||
let hoisted_scope = self.get_scope_for_declaration(scope, kind);
|
||||
let scope = self.scope(scope_id);
|
||||
// Determine the scope to which this declaration should be hoisted. This mainly applies to var declarations
|
||||
let hoisted_scope_id = self.get_scope_for_declaration(scope_id, kind);
|
||||
|
||||
// Check for redeclaration. The rules are roughly:
|
||||
// * `var` can be redeclared any number of times in a given scope. These redeclarations have no effect,
|
||||
// subsequent declarations are equivalent to just reassigning a value to the original declaration.
|
||||
// ie `var a = 1; var a = 2;` is equivalent to `var a; a = 1; a = 2`.
|
||||
// * Other forms (in strict mode) may not be redeclared in a given scope.
|
||||
// * This implies that `var` cannot conflict with other types of declarations, either in the scope
|
||||
// at which they are declared or the scope to which the var will hoist:
|
||||
// * `function() { {let a; var a;} }` conflicts at the declaration scope, even though the var will hoise above.
|
||||
// * `function() { let a; { var a; } }` conflicts bc the var hoists to the scope w a conflicting let.
|
||||
match kind {
|
||||
DeclarationKind::Var => {
|
||||
if let Some(declaration) = scope.declarations.get(&name) {
|
||||
let declaration = self.declaration(*declaration);
|
||||
if is_block_scoped_declaration(declaration.kind) {
|
||||
// Var cannot be declared in the same scope as let/const/class/import/etc
|
||||
self.diagnostics
|
||||
.push(Diagnostic::invalid_syntax("Duplicate declaration", range));
|
||||
}
|
||||
} else if hoisted_scope_id != scope_id {
|
||||
if let Some(declaration) = self.scope(hoisted_scope_id).declarations.get(&name)
|
||||
{
|
||||
let declaration = self.declaration(*declaration);
|
||||
if is_block_scoped_declaration(declaration.kind) {
|
||||
// Var cannot *hoist* to the same scope as let/const/class/import/etc
|
||||
self.diagnostics
|
||||
.push(Diagnostic::invalid_syntax("Duplicate declaration", range));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Redeclaration of `var` in a given scope has no effect, subsequent declarations
|
||||
// are equivalent to re-declarations
|
||||
// ie `var a = 1; var a = 2;` is equivalent to `var a; a = 1; a = 2`.
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var#redeclarations
|
||||
if let Some(declaration) = self.scope(hoisted_scope_id).declarations.get(&name) {
|
||||
let declaration = self.declaration(*declaration);
|
||||
if declaration.kind == DeclarationKind::Var {
|
||||
return declaration.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
DeclarationKind::CatchClause
|
||||
| DeclarationKind::Let
|
||||
| DeclarationKind::Const
|
||||
| DeclarationKind::Import
|
||||
| DeclarationKind::Class
|
||||
| DeclarationKind::Function => {
|
||||
// When duplicate declarations occur we report an error and then resolve references to the
|
||||
// first declaration. It doesn't really matter which declaration we refer to, because
|
||||
// semantic results are invalid if there are errors. The main consideration is that we do
|
||||
// not want to report a "cannot find declaration for `x`" reference error just because there
|
||||
// were duplicate declarations of `x`.
|
||||
if let Some(_declaration) = scope.declarations.get(&name) {
|
||||
self.diagnostics
|
||||
.push(Diagnostic::invalid_syntax("Duplicate declaration", range));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Always create a new declaration and id...
|
||||
let id = DeclarationId(self.declarations.len());
|
||||
self.declarations.push(Declaration {
|
||||
id,
|
||||
kind,
|
||||
name: name.clone(),
|
||||
scope: hoisted_scope,
|
||||
scope: hoisted_scope_id,
|
||||
});
|
||||
self.scopes[hoisted_scope.0].declarations.insert(name, id);
|
||||
// ...but only save the first declaration for a given name in each scope
|
||||
self.scopes[hoisted_scope_id.0]
|
||||
.declarations
|
||||
.entry(name)
|
||||
.or_insert(id);
|
||||
id
|
||||
}
|
||||
|
||||
@@ -294,8 +346,8 @@ impl ScopeManager {
|
||||
| DeclarationKind::Import
|
||||
| DeclarationKind::Const
|
||||
| DeclarationKind::CatchClause
|
||||
| DeclarationKind::For => scope,
|
||||
DeclarationKind::Var | DeclarationKind::FunctionDeclaration => {
|
||||
| DeclarationKind::Class => scope,
|
||||
DeclarationKind::Var | DeclarationKind::Function => {
|
||||
let mut current = scope;
|
||||
loop {
|
||||
let scope = self.scope(current);
|
||||
@@ -342,6 +394,18 @@ impl ScopeManager {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_block_scoped_declaration(kind: DeclarationKind) -> bool {
|
||||
match kind {
|
||||
DeclarationKind::Let
|
||||
| DeclarationKind::Const
|
||||
| DeclarationKind::Import
|
||||
| DeclarationKind::Class
|
||||
| DeclarationKind::Function
|
||||
| DeclarationKind::CatchClause => true,
|
||||
DeclarationKind::Var => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone)]
|
||||
pub struct ScopeId(usize);
|
||||
|
||||
@@ -393,11 +457,11 @@ pub struct Label {
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone)]
|
||||
pub enum DeclarationKind {
|
||||
Class,
|
||||
Const,
|
||||
Var,
|
||||
Let,
|
||||
FunctionDeclaration,
|
||||
For,
|
||||
Function,
|
||||
CatchClause,
|
||||
Import,
|
||||
}
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
function Component() {
|
||||
let a = 1;
|
||||
let a = 2; // error
|
||||
|
||||
const b = 3;
|
||||
const b = 4; // error
|
||||
|
||||
function foo() {}
|
||||
function foo() {} // error
|
||||
|
||||
try {
|
||||
} catch (c) {
|
||||
let c = true; // error
|
||||
const c = true; // error
|
||||
function c() {} // error
|
||||
// class c {} // error
|
||||
}
|
||||
}
|
||||
|
||||
function Component() {
|
||||
// error
|
||||
}
|
||||
|
||||
const x = true;
|
||||
const x = false; // error
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
function Component() {
|
||||
let a;
|
||||
{
|
||||
var a; // error, conflicts when hoisted
|
||||
}
|
||||
|
||||
const b = 1;
|
||||
{
|
||||
var b; // error, conflicts
|
||||
}
|
||||
|
||||
{
|
||||
let c;
|
||||
var c; // error, conflicts
|
||||
}
|
||||
|
||||
{
|
||||
const d = 2;
|
||||
var d; // error, conflicts
|
||||
}
|
||||
|
||||
// there should be one instance of `e`:
|
||||
var e = 3;
|
||||
console.log(e); // 3
|
||||
var e = 4;
|
||||
console.log(e); // 4
|
||||
var e;
|
||||
console.log(e); // 4
|
||||
}
|
||||
+308
@@ -0,0 +1,308 @@
|
||||
---
|
||||
source: crates/forget_semantic_analysis/tests/analysis_test.rs
|
||||
expression: "format!(\"Input:\\n{input}\\n\\nAnalysis:\\n{output}\")"
|
||||
input_file: crates/forget_semantic_analysis/tests/fixtures/block-item-duplication.js
|
||||
---
|
||||
Input:
|
||||
function Component() {
|
||||
let a = 1;
|
||||
let a = 2; // error
|
||||
|
||||
const b = 3;
|
||||
const b = 4; // error
|
||||
|
||||
function foo() {}
|
||||
function foo() {} // error
|
||||
|
||||
try {
|
||||
} catch (c) {
|
||||
let c = true; // error
|
||||
const c = true; // error
|
||||
function c() {} // error
|
||||
// class c {} // error
|
||||
}
|
||||
}
|
||||
|
||||
function Component() {
|
||||
// error
|
||||
}
|
||||
|
||||
const x = true;
|
||||
const x = false; // error
|
||||
|
||||
|
||||
Analysis:
|
||||
Scope {
|
||||
id: ScopeId(
|
||||
0,
|
||||
),
|
||||
kind: Module,
|
||||
declarations: {
|
||||
"Component": Declaration {
|
||||
id: DeclarationId(
|
||||
0,
|
||||
),
|
||||
kind: Function,
|
||||
scope: ScopeId(
|
||||
0,
|
||||
),
|
||||
},
|
||||
"x": Declaration {
|
||||
id: DeclarationId(
|
||||
12,
|
||||
),
|
||||
kind: Const,
|
||||
scope: ScopeId(
|
||||
0,
|
||||
),
|
||||
},
|
||||
},
|
||||
references: [],
|
||||
children: [
|
||||
Scope {
|
||||
id: ScopeId(
|
||||
1,
|
||||
),
|
||||
kind: Function,
|
||||
declarations: {
|
||||
"a": Declaration {
|
||||
id: DeclarationId(
|
||||
1,
|
||||
),
|
||||
kind: Let,
|
||||
scope: ScopeId(
|
||||
1,
|
||||
),
|
||||
},
|
||||
"b": Declaration {
|
||||
id: DeclarationId(
|
||||
3,
|
||||
),
|
||||
kind: Const,
|
||||
scope: ScopeId(
|
||||
1,
|
||||
),
|
||||
},
|
||||
"foo": Declaration {
|
||||
id: DeclarationId(
|
||||
5,
|
||||
),
|
||||
kind: Function,
|
||||
scope: ScopeId(
|
||||
1,
|
||||
),
|
||||
},
|
||||
"c": Declaration {
|
||||
id: DeclarationId(
|
||||
10,
|
||||
),
|
||||
kind: Function,
|
||||
scope: ScopeId(
|
||||
1,
|
||||
),
|
||||
},
|
||||
},
|
||||
references: [],
|
||||
children: [
|
||||
Scope {
|
||||
id: ScopeId(
|
||||
2,
|
||||
),
|
||||
kind: Function,
|
||||
declarations: {},
|
||||
references: [],
|
||||
children: [],
|
||||
},
|
||||
Scope {
|
||||
id: ScopeId(
|
||||
3,
|
||||
),
|
||||
kind: Function,
|
||||
declarations: {},
|
||||
references: [],
|
||||
children: [],
|
||||
},
|
||||
Scope {
|
||||
id: ScopeId(
|
||||
4,
|
||||
),
|
||||
kind: Block,
|
||||
declarations: {},
|
||||
references: [],
|
||||
children: [],
|
||||
},
|
||||
Scope {
|
||||
id: ScopeId(
|
||||
5,
|
||||
),
|
||||
kind: CatchClause,
|
||||
declarations: {
|
||||
"c": Declaration {
|
||||
id: DeclarationId(
|
||||
7,
|
||||
),
|
||||
kind: CatchClause,
|
||||
scope: ScopeId(
|
||||
5,
|
||||
),
|
||||
},
|
||||
},
|
||||
references: [],
|
||||
children: [
|
||||
Scope {
|
||||
id: ScopeId(
|
||||
6,
|
||||
),
|
||||
kind: Block,
|
||||
declarations: {
|
||||
"c": Declaration {
|
||||
id: DeclarationId(
|
||||
8,
|
||||
),
|
||||
kind: Let,
|
||||
scope: ScopeId(
|
||||
6,
|
||||
),
|
||||
},
|
||||
},
|
||||
references: [],
|
||||
children: [
|
||||
Scope {
|
||||
id: ScopeId(
|
||||
7,
|
||||
),
|
||||
kind: Function,
|
||||
declarations: {},
|
||||
references: [],
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
Scope {
|
||||
id: ScopeId(
|
||||
8,
|
||||
),
|
||||
kind: Function,
|
||||
declarations: {},
|
||||
references: [],
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
}
|
||||
Diagnostic(
|
||||
DiagnosticData {
|
||||
message: "Duplicate declaration",
|
||||
span: Some(
|
||||
SourceSpan {
|
||||
offset: SourceOffset(
|
||||
42,
|
||||
),
|
||||
length: 1,
|
||||
},
|
||||
),
|
||||
related_information: [],
|
||||
severity: InvalidSyntax,
|
||||
data: [],
|
||||
},
|
||||
)
|
||||
Diagnostic(
|
||||
DiagnosticData {
|
||||
message: "Duplicate declaration",
|
||||
span: Some(
|
||||
SourceSpan {
|
||||
offset: SourceOffset(
|
||||
82,
|
||||
),
|
||||
length: 1,
|
||||
},
|
||||
),
|
||||
related_information: [],
|
||||
severity: InvalidSyntax,
|
||||
data: [],
|
||||
},
|
||||
)
|
||||
Diagnostic(
|
||||
DiagnosticData {
|
||||
message: "Duplicate declaration",
|
||||
span: Some(
|
||||
SourceSpan {
|
||||
offset: SourceOffset(
|
||||
130,
|
||||
),
|
||||
length: 3,
|
||||
},
|
||||
),
|
||||
related_information: [],
|
||||
severity: InvalidSyntax,
|
||||
data: [],
|
||||
},
|
||||
)
|
||||
Diagnostic(
|
||||
DiagnosticData {
|
||||
message: "Duplicate declaration",
|
||||
span: Some(
|
||||
SourceSpan {
|
||||
offset: SourceOffset(
|
||||
210,
|
||||
),
|
||||
length: 1,
|
||||
},
|
||||
),
|
||||
related_information: [],
|
||||
severity: InvalidSyntax,
|
||||
data: [],
|
||||
},
|
||||
)
|
||||
Diagnostic(
|
||||
DiagnosticData {
|
||||
message: "Duplicate declaration",
|
||||
span: Some(
|
||||
SourceSpan {
|
||||
offset: SourceOffset(
|
||||
242,
|
||||
),
|
||||
length: 1,
|
||||
},
|
||||
),
|
||||
related_information: [],
|
||||
severity: InvalidSyntax,
|
||||
data: [],
|
||||
},
|
||||
)
|
||||
Diagnostic(
|
||||
DiagnosticData {
|
||||
message: "Duplicate declaration",
|
||||
span: Some(
|
||||
SourceSpan {
|
||||
offset: SourceOffset(
|
||||
301,
|
||||
),
|
||||
length: 9,
|
||||
},
|
||||
),
|
||||
related_information: [],
|
||||
severity: InvalidSyntax,
|
||||
data: [],
|
||||
},
|
||||
)
|
||||
Diagnostic(
|
||||
DiagnosticData {
|
||||
message: "Duplicate declaration",
|
||||
span: Some(
|
||||
SourceSpan {
|
||||
offset: SourceOffset(
|
||||
351,
|
||||
),
|
||||
length: 1,
|
||||
},
|
||||
),
|
||||
related_information: [],
|
||||
severity: InvalidSyntax,
|
||||
data: [],
|
||||
},
|
||||
)
|
||||
|
||||
+4
-4
@@ -39,7 +39,7 @@ Scope {
|
||||
id: DeclarationId(
|
||||
0,
|
||||
),
|
||||
kind: FunctionDeclaration,
|
||||
kind: Function,
|
||||
scope: ScopeId(
|
||||
0,
|
||||
),
|
||||
@@ -57,7 +57,7 @@ Scope {
|
||||
id: DeclarationId(
|
||||
1,
|
||||
),
|
||||
kind: FunctionDeclaration,
|
||||
kind: Function,
|
||||
scope: ScopeId(
|
||||
1,
|
||||
),
|
||||
@@ -66,7 +66,7 @@ Scope {
|
||||
id: DeclarationId(
|
||||
2,
|
||||
),
|
||||
kind: FunctionDeclaration,
|
||||
kind: Function,
|
||||
scope: ScopeId(
|
||||
1,
|
||||
),
|
||||
@@ -191,7 +191,7 @@ Scope {
|
||||
id: DeclarationId(
|
||||
4,
|
||||
),
|
||||
kind: FunctionDeclaration,
|
||||
kind: Function,
|
||||
scope: ScopeId(
|
||||
9,
|
||||
),
|
||||
|
||||
+2
-2
@@ -61,7 +61,7 @@ Scope {
|
||||
id: DeclarationId(
|
||||
3,
|
||||
),
|
||||
kind: FunctionDeclaration,
|
||||
kind: Function,
|
||||
scope: ScopeId(
|
||||
0,
|
||||
),
|
||||
@@ -79,7 +79,7 @@ Scope {
|
||||
id: DeclarationId(
|
||||
4,
|
||||
),
|
||||
kind: FunctionDeclaration,
|
||||
kind: Function,
|
||||
scope: ScopeId(
|
||||
1,
|
||||
),
|
||||
|
||||
+2
-2
@@ -30,7 +30,7 @@ Scope {
|
||||
id: DeclarationId(
|
||||
0,
|
||||
),
|
||||
kind: FunctionDeclaration,
|
||||
kind: Function,
|
||||
scope: ScopeId(
|
||||
0,
|
||||
),
|
||||
@@ -48,7 +48,7 @@ Scope {
|
||||
id: DeclarationId(
|
||||
1,
|
||||
),
|
||||
kind: FunctionDeclaration,
|
||||
kind: Function,
|
||||
scope: ScopeId(
|
||||
1,
|
||||
),
|
||||
|
||||
+2
-2
@@ -25,7 +25,7 @@ Scope {
|
||||
id: DeclarationId(
|
||||
0,
|
||||
),
|
||||
kind: FunctionDeclaration,
|
||||
kind: Function,
|
||||
scope: ScopeId(
|
||||
0,
|
||||
),
|
||||
@@ -43,7 +43,7 @@ Scope {
|
||||
id: DeclarationId(
|
||||
1,
|
||||
),
|
||||
kind: FunctionDeclaration,
|
||||
kind: Function,
|
||||
scope: ScopeId(
|
||||
1,
|
||||
),
|
||||
|
||||
+4
-4
@@ -26,7 +26,7 @@ Scope {
|
||||
id: DeclarationId(
|
||||
0,
|
||||
),
|
||||
kind: FunctionDeclaration,
|
||||
kind: Function,
|
||||
scope: ScopeId(
|
||||
0,
|
||||
),
|
||||
@@ -44,7 +44,7 @@ Scope {
|
||||
id: DeclarationId(
|
||||
1,
|
||||
),
|
||||
kind: FunctionDeclaration,
|
||||
kind: Function,
|
||||
scope: ScopeId(
|
||||
1,
|
||||
),
|
||||
@@ -94,7 +94,7 @@ Scope {
|
||||
id: DeclarationId(
|
||||
4,
|
||||
),
|
||||
kind: FunctionDeclaration,
|
||||
kind: Function,
|
||||
scope: ScopeId(
|
||||
2,
|
||||
),
|
||||
@@ -112,7 +112,7 @@ Scope {
|
||||
id: DeclarationId(
|
||||
5,
|
||||
),
|
||||
kind: FunctionDeclaration,
|
||||
kind: Function,
|
||||
scope: ScopeId(
|
||||
3,
|
||||
),
|
||||
|
||||
+2
-2
@@ -31,7 +31,7 @@ Scope {
|
||||
id: DeclarationId(
|
||||
0,
|
||||
),
|
||||
kind: FunctionDeclaration,
|
||||
kind: Function,
|
||||
scope: ScopeId(
|
||||
0,
|
||||
),
|
||||
@@ -49,7 +49,7 @@ Scope {
|
||||
id: DeclarationId(
|
||||
1,
|
||||
),
|
||||
kind: FunctionDeclaration,
|
||||
kind: Function,
|
||||
scope: ScopeId(
|
||||
1,
|
||||
),
|
||||
|
||||
+323
@@ -0,0 +1,323 @@
|
||||
---
|
||||
source: crates/forget_semantic_analysis/tests/analysis_test.rs
|
||||
expression: "format!(\"Input:\\n{input}\\n\\nAnalysis:\\n{output}\")"
|
||||
input_file: crates/forget_semantic_analysis/tests/fixtures/var-duplication.js
|
||||
---
|
||||
Input:
|
||||
function Component() {
|
||||
let a;
|
||||
{
|
||||
var a; // error, conflicts when hoisted
|
||||
}
|
||||
|
||||
const b = 1;
|
||||
{
|
||||
var b; // error, conflicts
|
||||
}
|
||||
|
||||
{
|
||||
let c;
|
||||
var c; // error, conflicts
|
||||
}
|
||||
|
||||
{
|
||||
const d = 2;
|
||||
var d; // error, conflicts
|
||||
}
|
||||
|
||||
// there should be one instance of `e`:
|
||||
var e = 3;
|
||||
console.log(e); // 3
|
||||
var e = 4;
|
||||
console.log(e); // 4
|
||||
var e;
|
||||
console.log(e); // 4
|
||||
}
|
||||
|
||||
|
||||
Analysis:
|
||||
Scope {
|
||||
id: ScopeId(
|
||||
0,
|
||||
),
|
||||
kind: Module,
|
||||
declarations: {
|
||||
"Component": Declaration {
|
||||
id: DeclarationId(
|
||||
0,
|
||||
),
|
||||
kind: Function,
|
||||
scope: ScopeId(
|
||||
0,
|
||||
),
|
||||
},
|
||||
},
|
||||
references: [],
|
||||
children: [
|
||||
Scope {
|
||||
id: ScopeId(
|
||||
1,
|
||||
),
|
||||
kind: Function,
|
||||
declarations: {
|
||||
"a": Declaration {
|
||||
id: DeclarationId(
|
||||
1,
|
||||
),
|
||||
kind: Let,
|
||||
scope: ScopeId(
|
||||
1,
|
||||
),
|
||||
},
|
||||
"b": Declaration {
|
||||
id: DeclarationId(
|
||||
3,
|
||||
),
|
||||
kind: Const,
|
||||
scope: ScopeId(
|
||||
1,
|
||||
),
|
||||
},
|
||||
"c": Declaration {
|
||||
id: DeclarationId(
|
||||
6,
|
||||
),
|
||||
kind: Var,
|
||||
scope: ScopeId(
|
||||
1,
|
||||
),
|
||||
},
|
||||
"d": Declaration {
|
||||
id: DeclarationId(
|
||||
8,
|
||||
),
|
||||
kind: Var,
|
||||
scope: ScopeId(
|
||||
1,
|
||||
),
|
||||
},
|
||||
"e": Declaration {
|
||||
id: DeclarationId(
|
||||
9,
|
||||
),
|
||||
kind: Var,
|
||||
scope: ScopeId(
|
||||
1,
|
||||
),
|
||||
},
|
||||
},
|
||||
references: [
|
||||
Reference {
|
||||
id: ReferenceId(
|
||||
0,
|
||||
),
|
||||
kind: Read,
|
||||
declaration: DeclarationId(
|
||||
9,
|
||||
),
|
||||
declaration (name): "e",
|
||||
scope: ScopeId(
|
||||
1,
|
||||
),
|
||||
},
|
||||
Reference {
|
||||
id: ReferenceId(
|
||||
1,
|
||||
),
|
||||
kind: Read,
|
||||
declaration: DeclarationId(
|
||||
9,
|
||||
),
|
||||
declaration (name): "e",
|
||||
scope: ScopeId(
|
||||
1,
|
||||
),
|
||||
},
|
||||
Reference {
|
||||
id: ReferenceId(
|
||||
2,
|
||||
),
|
||||
kind: Read,
|
||||
declaration: DeclarationId(
|
||||
9,
|
||||
),
|
||||
declaration (name): "e",
|
||||
scope: ScopeId(
|
||||
1,
|
||||
),
|
||||
},
|
||||
],
|
||||
children: [
|
||||
Scope {
|
||||
id: ScopeId(
|
||||
2,
|
||||
),
|
||||
kind: Block,
|
||||
declarations: {},
|
||||
references: [],
|
||||
children: [],
|
||||
},
|
||||
Scope {
|
||||
id: ScopeId(
|
||||
3,
|
||||
),
|
||||
kind: Block,
|
||||
declarations: {},
|
||||
references: [],
|
||||
children: [],
|
||||
},
|
||||
Scope {
|
||||
id: ScopeId(
|
||||
4,
|
||||
),
|
||||
kind: Block,
|
||||
declarations: {
|
||||
"c": Declaration {
|
||||
id: DeclarationId(
|
||||
5,
|
||||
),
|
||||
kind: Let,
|
||||
scope: ScopeId(
|
||||
4,
|
||||
),
|
||||
},
|
||||
},
|
||||
references: [],
|
||||
children: [],
|
||||
},
|
||||
Scope {
|
||||
id: ScopeId(
|
||||
5,
|
||||
),
|
||||
kind: Block,
|
||||
declarations: {
|
||||
"d": Declaration {
|
||||
id: DeclarationId(
|
||||
7,
|
||||
),
|
||||
kind: Const,
|
||||
scope: ScopeId(
|
||||
5,
|
||||
),
|
||||
},
|
||||
},
|
||||
references: [],
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
Diagnostic(
|
||||
DiagnosticData {
|
||||
message: "Duplicate declaration",
|
||||
span: Some(
|
||||
SourceSpan {
|
||||
offset: SourceOffset(
|
||||
44,
|
||||
),
|
||||
length: 1,
|
||||
},
|
||||
),
|
||||
related_information: [],
|
||||
severity: InvalidSyntax,
|
||||
data: [],
|
||||
},
|
||||
)
|
||||
Diagnostic(
|
||||
DiagnosticData {
|
||||
message: "Duplicate declaration",
|
||||
span: Some(
|
||||
SourceSpan {
|
||||
offset: SourceOffset(
|
||||
112,
|
||||
),
|
||||
length: 1,
|
||||
},
|
||||
),
|
||||
related_information: [],
|
||||
severity: InvalidSyntax,
|
||||
data: [],
|
||||
},
|
||||
)
|
||||
Diagnostic(
|
||||
DiagnosticData {
|
||||
message: "Duplicate declaration",
|
||||
span: Some(
|
||||
SourceSpan {
|
||||
offset: SourceOffset(
|
||||
163,
|
||||
),
|
||||
length: 1,
|
||||
},
|
||||
),
|
||||
related_information: [],
|
||||
severity: InvalidSyntax,
|
||||
data: [],
|
||||
},
|
||||
)
|
||||
Diagnostic(
|
||||
DiagnosticData {
|
||||
message: "Duplicate declaration",
|
||||
span: Some(
|
||||
SourceSpan {
|
||||
offset: SourceOffset(
|
||||
220,
|
||||
),
|
||||
length: 1,
|
||||
},
|
||||
),
|
||||
related_information: [],
|
||||
severity: InvalidSyntax,
|
||||
data: [],
|
||||
},
|
||||
)
|
||||
Diagnostic(
|
||||
DiagnosticData {
|
||||
message: "Undefined variable",
|
||||
span: Some(
|
||||
SourceSpan {
|
||||
offset: SourceOffset(
|
||||
305,
|
||||
),
|
||||
length: 7,
|
||||
},
|
||||
),
|
||||
related_information: [],
|
||||
severity: InvalidSyntax,
|
||||
data: [],
|
||||
},
|
||||
)
|
||||
Diagnostic(
|
||||
DiagnosticData {
|
||||
message: "Undefined variable",
|
||||
span: Some(
|
||||
SourceSpan {
|
||||
offset: SourceOffset(
|
||||
341,
|
||||
),
|
||||
length: 7,
|
||||
},
|
||||
),
|
||||
related_information: [],
|
||||
severity: InvalidSyntax,
|
||||
data: [],
|
||||
},
|
||||
)
|
||||
Diagnostic(
|
||||
DiagnosticData {
|
||||
message: "Undefined variable",
|
||||
span: Some(
|
||||
SourceSpan {
|
||||
offset: SourceOffset(
|
||||
373,
|
||||
),
|
||||
length: 7,
|
||||
},
|
||||
),
|
||||
related_information: [],
|
||||
severity: InvalidSyntax,
|
||||
data: [],
|
||||
},
|
||||
)
|
||||
|
||||
+3
-3
@@ -36,7 +36,7 @@ Scope {
|
||||
id: DeclarationId(
|
||||
0,
|
||||
),
|
||||
kind: FunctionDeclaration,
|
||||
kind: Function,
|
||||
scope: ScopeId(
|
||||
0,
|
||||
),
|
||||
@@ -63,7 +63,7 @@ Scope {
|
||||
id: DeclarationId(
|
||||
1,
|
||||
),
|
||||
kind: FunctionDeclaration,
|
||||
kind: Function,
|
||||
scope: ScopeId(
|
||||
1,
|
||||
),
|
||||
@@ -72,7 +72,7 @@ Scope {
|
||||
id: DeclarationId(
|
||||
2,
|
||||
),
|
||||
kind: FunctionDeclaration,
|
||||
kind: Function,
|
||||
scope: ScopeId(
|
||||
1,
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user