Issue #1547 'use strict' duplicate formal parameter are prohibited

updated error message (thanks @davidchambers)

code style fixes
This commit is contained in:
Gerald Lewis 2012-01-09 12:56:18 -05:00
parent a2ef66f197
commit 7521068ba3
2 changed files with 55 additions and 12 deletions

View File

@ -1162,8 +1162,10 @@ exports.Code = class Code extends Base
o.scope.shared = del(o, 'sharedScope')
o.indent += TAB
delete o.bare
vars = []
exprs = []
params = []
exprs = []
for name in @paramNames() # this step must be performed before the others
unless o.scope.check name then o.scope.parameter name
for param in @params when param.splat
o.scope.add p.name.value, 'var', yes for p in @params when p.name.value
splats = new Assign new Value(new Arr(p.asReference o for p in @params)),
@ -1180,11 +1182,15 @@ exports.Code = class Code extends Base
lit = new Literal ref.name.value + ' == null'
val = new Assign new Value(param.name), param.value, '='
exprs.push new If lit, val
vars.push ref unless splats
params.push ref unless splats
wasEmpty = @body.isEmpty()
exprs.unshift splats if splats
@body.expressions.unshift exprs... if exprs.length
o.scope.parameter vars[i] = v.compile o for v, i in vars unless splats
o.scope.parameter params[i] = p.compile o for p, i in params
uniqs = []
for name in @paramNames()
throw SyntaxError "multiple parameters named '#{name}'" if name in uniqs
uniqs.push name
@body.makeReturn() unless wasEmpty or @noReturn
if @bound
if o.scope.parent.method?.bound
@ -1194,12 +1200,18 @@ exports.Code = class Code extends Base
idt = o.indent
code = 'function'
code += ' ' + @name if @ctor
code += '(' + vars.join(', ') + ') {'
code += '(' + params.join(', ') + ') {'
code += "\n#{ @body.compileWithDeclarations o }\n#{@tab}" unless @body.isEmpty()
code += '}'
return @tab + code if @ctor
if @front or (o.level >= LEVEL_ACCESS) then "(#{code})" else code
# A list of parameter names, excluding those generated by the compiler.
paramNames: ->
names = []
names.push param.names()... for param in @params
names
# Short-circuit `traverseChildren` method to prevent it from crossing scope boundaries
# unless `crossScope` is `true`.
traverseChildren: (crossScope, func) ->
@ -1223,7 +1235,8 @@ exports.Param = class Param extends Base
node = @name
if node.this
node = node.properties[0].name
node = new Literal '_' + node.value if node.value.reserved
if node.value.reserved
node = new Literal o.scope.freeVariable node.value
else if node.isComplex()
node = new Literal o.scope.freeVariable 'arg'
node = new Value node
@ -1233,6 +1246,36 @@ exports.Param = class Param extends Base
isComplex: ->
@name.isComplex()
# Finds the name or names of a `Param`; useful for detecting duplicates.
# In a sense, a destructured parameter represents multiple JS parameters,
# thus this method returns an `Array` of names.
# Reserved words used as param names, as well as the Object and Array
# literals used for destructured params, get a compiler generated name
# during the `Code` compilation step, so this is necessarily an incomplete
# list of a parameter's names.
names: (name = @name)->
atParam = (obj) ->
{value} = obj.properties[0].name
return if value.reserved then [] else [value]
# * simple literals `foo`
return [name.value] if name instanceof Literal
# * at-params `@foo`
return atParam(name) if name instanceof Value
names = []
for obj in name.objects
# * assignments within destructured parameters `{foo:bar}`
if obj instanceof Assign
names.push obj.variable.base.value
# * destructured parameters within destructured parameters `[{a}]`
else if obj.isArray() or obj.isObject()
names.push @names(obj.base)...
# * at-params within destructured parameters `{@foo}`
else if obj.this
names.push atParam(obj)...
# * simple destructured parameters {foo}
else names.push obj.base.value
names
#### Splat
# A splat, either as a parameter to a function, an argument to a call,

View File

@ -91,9 +91,9 @@ test "self-referencing functions", ->
test "splats", ->
arrayEq [0, 1, 2], (((splat...) -> splat) 0, 1, 2)
arrayEq [2, 3], (((_, _, splat...) -> splat) 0, 1, 2, 3)
arrayEq [0, 1], (((splat..., _, _) -> splat) 0, 1, 2, 3)
arrayEq [2], (((_, _, splat..., _) -> splat) 0, 1, 2, 3)
arrayEq [2, 3], (((_, _1, splat...) -> splat) 0, 1, 2, 3)
arrayEq [0, 1], (((splat..., _, _1) -> splat) 0, 1, 2, 3)
arrayEq [2], (((_, _1, splat..., _2) -> splat) 0, 1, 2, 3)
test "@-parameters: automatically assign an argument's value to a property of the context", ->
nonce = {}
@ -131,7 +131,7 @@ test "destructuring in function definition", ->
test "default values", ->
nonceA = {}
nonceB = {}
a = (_,_,arg=nonceA) -> arg
a = (_,_1,arg=nonceA) -> arg
eq nonceA, a()
eq nonceA, a(0)
eq nonceB, a(0,0,nonceB)
@ -139,7 +139,7 @@ test "default values", ->
eq nonceA, a(0,0,null)
eq false , a(0,0,false)
eq nonceB, a(undefined,undefined,nonceB,undefined)
b = (_,arg=nonceA,_,_) -> arg
b = (_,arg=nonceA,_1,_2) -> arg
eq nonceA, b()
eq nonceA, b(0)
eq nonceB, b(0,nonceB)
@ -147,7 +147,7 @@ test "default values", ->
eq nonceA, b(0,null)
eq false , b(0,false)
eq nonceB, b(undefined,nonceB,undefined)
c = (arg=nonceA,_,_) -> arg
c = (arg=nonceA,_,_1) -> arg
eq nonceA, c()
eq 0, c(0)
eq nonceB, c(nonceB)