Skip to content

Commit

Permalink
Allow functions to redeclare vars and functions in function scopes
Browse files Browse the repository at this point in the history
  • Loading branch information
lukastaegert committed Nov 13, 2023
1 parent 53d6360 commit a2e2593
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 5 deletions.
8 changes: 6 additions & 2 deletions src/ast/scopes/ModuleScope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,12 @@ export default class ModuleScope extends ChildScope {
init: ExpressionEntity,
kind: VariableKind
): LocalVariable {
if (this.context.module.importDescriptions.has(identifier.name)) {
context.error(logRedeclarationError(identifier.name), identifier.start);
const name = identifier.name;
if (
this.context.module.importDescriptions.has(name) ||
(this.variables.has(name) && kind === VariableKind.function)
) {
context.error(logRedeclarationError(name), identifier.start);
}
return super.addDeclaration(identifier, context, init, kind);
}
Expand Down
8 changes: 5 additions & 3 deletions src/ast/scopes/Scope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export default class Scope {
/*
Redeclaration rules:
- var can redeclare var
- in function scopes, function and var can redeclare function and var
- var is hoisted across scopes, function remains in the scope it is declared
- var and function can redeclare function parameters, but parameters cannot redeclare parameters
- function cannot redeclare catch scope parameters
Expand All @@ -35,9 +36,10 @@ export default class Scope {
if (existingVariable) {
const existingKind = existingVariable.kind;
if (
(kind === VariableKind.var &&
(existingKind === VariableKind.var || existingKind === VariableKind.parameter)) ||
(kind === VariableKind.function && existingKind === VariableKind.parameter)
(kind === VariableKind.var || kind === VariableKind.function) &&
(existingKind === VariableKind.var ||
existingKind === VariableKind.function ||
existingKind === VariableKind.parameter)
) {
existingVariable.addDeclaration(identifier, init);
return existingVariable;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const path = require('node:path');
const ID_MAIN = path.join(__dirname, 'main.js');

module.exports = defineTest({
description: 'throws when redeclaring a top-level function binding as a function',
error: {
code: 'REDECLARATION_ERROR',
frame: `
1: function foo() {}
2: function foo() {}
^`,
id: ID_MAIN,
loc: {
column: 9,
file: ID_MAIN,
line: 2
},
message: 'Identifier "foo" has already been declared',
pos: 27,
watchFiles: [ID_MAIN]
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
function foo() {}
function foo() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const path = require('node:path');
const ID_MAIN = path.join(__dirname, 'main.js');

module.exports = defineTest({
description: 'throws when redeclaring a top-level var binding as a function',
error: {
code: 'REDECLARATION_ERROR',
frame: `
1: var foo;
2: function foo() {}
^`,
id: ID_MAIN,
loc: {
column: 9,
file: ID_MAIN,
line: 2
},
message: 'Identifier "foo" has already been declared',
pos: 18,
watchFiles: [ID_MAIN]
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
var foo;
function foo() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = defineTest({
description: 'allows to redeclare vars and functions as vars and functions in function scopes'
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
function foo() {
var fn = 1;
function fn() {}
function fn() {}
var fn = 2;

assert.equal(fn, 2);
}

foo();

0 comments on commit a2e2593

Please sign in to comment.