Skip to content

Commit

Permalink
"UniqueFormalParameters" for arrows and methods
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Feb 18, 2021
1 parent 81436d4 commit 92c6559
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 10 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@

* According to the standard, all code inside a class statement or expression should be in strict mode. Previously esbuild treated code inside a class as the same strict mode status as the surrounding code, but now code in a class is always interpreted as strict mode code.

* Duplicate bindings in the same parameter list are not allowed if the parameter list isn't simple, such as in the code `function f(a, [a]) {}`. This rule is now enforced by esbuild's parser, so doing this is now a syntax error.
* Duplicate bindings in the same parameter list are not allowed if the parameter list isn't simple, such as in the code `function f(a, [a]) {}`, or if the parameter list belongs to an arrow function or a method. This rule is now enforced by esbuild's parser, so doing this is now a syntax error.

* Remove the local web server feature from the WebAssembly package ([#836](https://github.com/evanw/esbuild/issues/836))

Expand Down
3 changes: 3 additions & 0 deletions internal/js_ast/js_ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,9 @@ type Fn struct {
IsGenerator bool
HasRestArg bool
HasIfScope bool

// This is true if the function is a method
IsUniqueFormalParameters bool
}

type FnBody struct {
Expand Down
29 changes: 23 additions & 6 deletions internal/js_parser/js_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -1905,6 +1905,7 @@ func (p *parser) parseProperty(kind js_ast.PropertyKind, opts propertyOpts, erro
}

p.popScope()
fn.IsUniqueFormalParameters = true
value := js_ast.Expr{Loc: loc, Data: &js_ast.EFunction{Fn: fn}}

// Enforce argument rules for accessors
Expand Down Expand Up @@ -8932,10 +8933,18 @@ func fnBodyContainsUseStrict(body []js_ast.Stmt) (logger.Loc, bool) {
return logger.Loc{}, false
}

func (p *parser) visitArgs(args []js_ast.Arg, hasRestArg bool, body []js_ast.Stmt) {
type visitArgsOpts struct {
body []js_ast.Stmt
hasRestArg bool

// This is true if the function is an arrow function or a method
isUniqueFormalParameters bool
}

func (p *parser) visitArgs(args []js_ast.Arg, opts visitArgsOpts) {
var duplicateArgCheck map[string]bool
useStrictLoc, hasUseStrict := fnBodyContainsUseStrict(body)
hasSimpleArgs := isSimpleParameterList(args, hasRestArg)
useStrictLoc, hasUseStrict := fnBodyContainsUseStrict(opts.body)
hasSimpleArgs := isSimpleParameterList(args, opts.hasRestArg)

// Section 15.2.1 Static Semantics: Early Errors: "It is a Syntax Error if
// FunctionBodyContainsUseStrict of FunctionBody is true and
Expand All @@ -8949,7 +8958,7 @@ func (p *parser) visitArgs(args []js_ast.Arg, hasRestArg bool, body []js_ast.Stm
// the same BindingIdentifier in a FormalParameterList is only allowed for
// functions which have simple parameter lists and which are not defined in
// strict mode code."
if p.isStrictMode() || !hasSimpleArgs {
if opts.isUniqueFormalParameters || !hasSimpleArgs || p.isStrictMode() {
duplicateArgCheck = make(map[string]bool)
}

Expand Down Expand Up @@ -11169,7 +11178,11 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO
}

p.pushScopeForVisitPass(js_ast.ScopeFunctionArgs, expr.Loc)
p.visitArgs(e.Args, e.HasRestArg, e.Body.Stmts)
p.visitArgs(e.Args, visitArgsOpts{
hasRestArg: e.HasRestArg,
body: e.Body.Stmts,
isUniqueFormalParameters: true,
})
p.pushScopeForVisitPass(js_ast.ScopeFunctionBody, e.Body.Loc)
e.Body.Stmts = p.visitStmtsAndPrependTempRefs(e.Body.Stmts, prependTempRefsOpts{kind: stmtsFnBody})
p.popScope()
Expand Down Expand Up @@ -11339,7 +11352,11 @@ func (p *parser) visitFn(fn *js_ast.Fn, scopeLoc logger.Loc) {
}

p.pushScopeForVisitPass(js_ast.ScopeFunctionArgs, scopeLoc)
p.visitArgs(fn.Args, fn.HasRestArg, fn.Body.Stmts)
p.visitArgs(fn.Args, visitArgsOpts{
hasRestArg: fn.HasRestArg,
body: fn.Body.Stmts,
isUniqueFormalParameters: fn.IsUniqueFormalParameters,
})
p.pushScopeForVisitPass(js_ast.ScopeFunctionBody, fn.Body.Loc)
fn.Body.Stmts = p.visitStmtsAndPrependTempRefs(fn.Body.Stmts, prependTempRefsOpts{fnBodyLoc: &fn.Body.Loc, kind: stmtsFnBody})
p.popScope()
Expand Down
10 changes: 7 additions & 3 deletions internal/js_parser/js_parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,9 @@ func TestStrictMode(t *testing.T) {

expectPrinted(t, "function f(a, a) {}", "function f(a, a) {\n}\n")
expectPrinted(t, "(function(a, a) {})", "(function(a, a) {\n});\n")
expectPrinted(t, "(a, a) => {}", "(a, a) => {\n};\n")
expectPrinted(t, "({ f: function(a, a) {} })", "({f: function(a, a) {\n}});\n")
expectPrinted(t, "({ f: function*(a, a) {} })", "({f: function* (a, a) {\n}});\n")
expectPrinted(t, "({ f: async function(a, a) {} })", "({f: async function(a, a) {\n}});\n")

expectParseError(t, "function f(a, [a]) {}", "<stdin>: error: \"a\" cannot be bound multiple times in the same parameter list\n")
expectParseError(t, "function f([a], a) {}", "<stdin>: error: \"a\" cannot be bound multiple times in the same parameter list\n")
Expand All @@ -263,8 +265,10 @@ func TestStrictMode(t *testing.T) {
expectParseError(t, "function f(a, a) {}; export {}", "<stdin>: error: \"a\" cannot be bound multiple times in the same parameter list\n")
expectParseError(t, "(function(a, a) {}); export {}", "<stdin>: error: \"a\" cannot be bound multiple times in the same parameter list\n")
expectParseError(t, "(function(a, [a]) {})", "<stdin>: error: \"a\" cannot be bound multiple times in the same parameter list\n")
expectParseError(t, "((a, a) => {}); export {}", "<stdin>: error: \"a\" cannot be bound multiple times in the same parameter list\n")
expectParseError(t, "((a, [a]) => {})", "<stdin>: error: \"a\" cannot be bound multiple times in the same parameter list\n")
expectParseError(t, "({ f(a, a) {} })", "<stdin>: error: \"a\" cannot be bound multiple times in the same parameter list\n")
expectParseError(t, "({ *f(a, a) {} })", "<stdin>: error: \"a\" cannot be bound multiple times in the same parameter list\n")
expectParseError(t, "({ async f(a, a) {} })", "<stdin>: error: \"a\" cannot be bound multiple times in the same parameter list\n")
expectParseError(t, "(a, a) => {}", "<stdin>: error: \"a\" cannot be bound multiple times in the same parameter list\n")

expectParseError(t, "'use strict'; if (0) function f() {}", "<stdin>: error: Function declarations inside if statements cannot be used in strict mode\n")
expectParseError(t, "'use strict'; if (0) ; else function f() {}", "<stdin>: error: Function declarations inside if statements cannot be used in strict mode\n")
Expand Down

0 comments on commit 92c6559

Please sign in to comment.