Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions crates/oxc_linter/src/snapshots/eslint_no_redeclare.snap
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ source: crates/oxc_linter/src/tester.rs
· ╰── `a` has already been declared here
╰────

× Identifier `a` has already been declared
⚠ eslint(no-redeclare): 'a' is already defined.
╭─[no_redeclare.tsx:1:10]
1 │ function a() {} function a() {}
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `a` has already been declared here
· │ ╰── It can not be redeclare here.
· ╰── 'a' is already defined.
╰────

⚠ eslint(no-redeclare): 'a' is already defined.
Expand Down
47 changes: 9 additions & 38 deletions crates/oxc_semantic/src/binder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::ptr;

use oxc_ast::{AstKind, ast::*};
use oxc_ecmascript::{BoundNames, IsSimpleParameterList};
use oxc_span::{GetSpan, SourceType};
use oxc_span::GetSpan;
use oxc_syntax::{
scope::{ScopeFlags, ScopeId},
symbol::SymbolFlags,
Expand Down Expand Up @@ -133,24 +133,6 @@ impl<'a> Binder<'a> for Class<'a> {
}
}

// It is a Syntax Error if the LexicallyDeclaredNames of StatementList contains any duplicate entries,
// unless the source text matched by this production is not strict mode code
// and the duplicate entries are only bound by FunctionDeclarations.
// https://tc39.es/ecma262/#sec-block-level-function-declarations-web-legacy-compatibility-semantics
fn function_as_var(flags: ScopeFlags, source_type: SourceType) -> bool {
flags.is_function() || (source_type.is_script() && flags.is_top())
}

/// Check if the function is not allowed to be redeclared.
pub fn is_function_redeclared_not_allowed(
func: &Function<'_>,
builder: &SemanticBuilder<'_>,
) -> bool {
let current_scope_flags = builder.current_scope_flags();
(current_scope_flags.is_strict_mode() || func.r#async || func.generator)
&& !function_as_var(current_scope_flags, builder.source_type)
}

/// Check for Annex B `if (foo) function a() {} else function b() {}`
fn is_function_part_of_if_statement(function: &Function, builder: &SemanticBuilder) -> bool {
if builder.current_scope_flags().is_strict_mode() {
Expand Down Expand Up @@ -189,29 +171,18 @@ impl<'a> Binder<'a> for Function<'a> {
);
ident.symbol_id.set(Some(symbol_id));
} else {
let excludes = if self.is_declaration() {
// The visitor is already inside the function scope,
// retrieve the parent scope for the function id to bind to.
if is_function_redeclared_not_allowed(self, builder) {
SymbolFlags::BlockScopedVariableExcludes
} else {
SymbolFlags::FunctionScopedVariableExcludes
}
} else if self.is_expression() {
// https://tc39.es/ecma262/#sec-runtime-semantics-instantiateordinaryfunctionexpression
// 5. Perform ! funcEnv.CreateImmutableBinding(name, false).
SymbolFlags::empty()
} else {
unreachable!(
"Currently we haven't create a symbol for typescript syntax function"
);
};

let symbol_id = builder.declare_symbol(
ident.span,
&ident.name,
SymbolFlags::Function,
excludes,
if builder.source_type.is_typescript() {
SymbolFlags::FunctionExcludes
} else {
// `var x; function x() {}` is valid in non-strict mode, but `TypeScript`
// doesn't care about non-strict mode, so we need to exclude this,
// and further check in checker.
SymbolFlags::FunctionExcludes - SymbolFlags::FunctionScopedVariable
},
);
ident.symbol_id.set(Some(symbol_id));
}
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_syntax/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ bitflags! {
/// Block-scoped declarations are not allowed to be re-declared
/// they can not merge with anything in the value space
const BlockScopedVariableExcludes = Self::Value.bits();

const FunctionExcludes = Self::Value.bits() & !(Self::Function.bits() | Self::ValueModule.bits() | Self::Class.bits());
const ClassExcludes = (Self::Value.bits() | Self::TypeAlias.bits()) & !(Self::ValueModule.bits() | Self::Interface.bits());
const ImportBindingExcludes = Self::Import.bits() | Self::TypeImport.bits();
// Type specific excludes
Expand Down
48 changes: 0 additions & 48 deletions tasks/coverage/snapshots/parser_babel.snap
Original file line number Diff line number Diff line change
Expand Up @@ -273,18 +273,6 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/es2022
× Identifier `x` has already been declared
╭─[babel/packages/babel-parser/test/fixtures/es2022/class-static-block/duplicate-function-var-name/input.js:3:11]
2 │ static {
3 │ var x;
· ┬
· ╰── `x` has already been declared here
4 │ function x() {}
· ┬
· ╰── It can not be redeclared here
5 │ }
╰────

× Identifier `x` has already been declared
╭─[babel/packages/babel-parser/test/fixtures/es2022/class-static-block/duplicate-function-var-name/input.js:3:11]
2 │ static {
3 │ var x;
· ┬
· ╰── `x` has already been declared here
Expand Down Expand Up @@ -1345,16 +1333,6 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
· ╰── It can not be redeclared here
╰────

× Identifier `foo` has already been declared
╭─[babel/packages/babel-parser/test/fixtures/core/scope/dupl-bind-class-func/input.js:1:7]
1 │ class foo {};
· ─┬─
· ╰── `foo` has already been declared here
2 │ function foo () {};
· ─┬─
· ╰── It can not be redeclared here
╰────

× Identifier `foo` has already been declared
╭─[babel/packages/babel-parser/test/fixtures/core/scope/dupl-bind-class-let/input.js:1:7]
1 │ class foo {};
Expand Down Expand Up @@ -1391,24 +1369,6 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
· ╰── `f` has already been declared here
╰────

× Identifier `f` has already been declared
╭─[babel/packages/babel-parser/test/fixtures/core/scope/dupl-bind-func-gen/input.js:1:12]
1 │ { function f() {} function* f() {} }
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `f` has already been declared here
╰────

× Identifier `foo` has already been declared
╭─[babel/packages/babel-parser/test/fixtures/core/scope/dupl-bind-func-module/input.js:1:10]
1 │ function foo() {}
· ─┬─
· ╰── `foo` has already been declared here
2 │ function foo() {}
· ─┬─
· ╰── It can not be redeclared here
╰────

× Identifier `foo` has already been declared
╭─[babel/packages/babel-parser/test/fixtures/core/scope/dupl-bind-func-module/input.js:1:10]
1 │ function foo() {}
Expand All @@ -1427,14 +1387,6 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
· ╰── `foo` has already been declared here
╰────

× Identifier `foo` has already been declared
╭─[babel/packages/babel-parser/test/fixtures/core/scope/dupl-bind-func-module-sloppy/input.js:1:12]
1 │ { function foo() {} function foo() {} }
· ─┬─ ─┬─
· │ ╰── It can not be redeclared here
· ╰── `foo` has already been declared here
╰────

× Identifier `foo` has already been declared
╭─[babel/packages/babel-parser/test/fixtures/core/scope/dupl-bind-func-var-sloppy/input.js:2:12]
1 │ {
Expand Down
Loading