Merge pull request #3784 from lydell/unique-generated-vars

Unique generated vars
This commit is contained in:
Michael Ficarra 2015-01-12 21:14:44 -08:00
commit 9fa77af576
15 changed files with 255 additions and 131 deletions

View File

@ -40,13 +40,25 @@
};
exports.compile = compile = withPrettyErrors(function(code, options) {
var answer, currentColumn, currentLine, extend, fragment, fragments, header, js, map, merge, newLines, _i, _len;
var answer, currentColumn, currentLine, extend, fragment, fragments, header, js, map, merge, newLines, token, tokens, _i, _len;
merge = helpers.merge, extend = helpers.extend;
options = extend({}, options);
if (options.sourceMap) {
map = new SourceMap;
}
fragments = parser.parse(lexer.tokenize(code, options)).compileToFragments(options);
tokens = lexer.tokenize(code, options);
options.referencedVars = (function() {
var _i, _len, _results;
_results = [];
for (_i = 0, _len = tokens.length; _i < _len; _i++) {
token = tokens[_i];
if (token.variable && token[1].charAt(0) === '_') {
_results.push(token[1]);
}
}
return _results;
})();
fragments = parser.parse(tokens).compileToFragments(options);
currentLine = 0;
if (options.header) {
currentLine += 1;
@ -236,8 +248,8 @@
}
return tag;
},
setInput: function(tokens) {
this.tokens = tokens;
setInput: function(_at_tokens) {
this.tokens = _at_tokens;
return this.pos = 0;
},
upcomingInput: function() {

View File

@ -139,6 +139,7 @@
})();
}
tagToken = this.token(tag, id, 0, idLength);
tagToken.variable = !forcedIdentifier;
if (poppedToken) {
_ref4 = [poppedToken[2].first_line, poppedToken[2].first_column], tagToken[2].first_line = _ref4[0], tagToken[2].first_column = _ref4[1];
}
@ -812,7 +813,7 @@
COFFEE_KEYWORDS = COFFEE_KEYWORDS.concat(COFFEE_ALIASES);
RESERVED = ['case', 'default', 'function', 'var', 'void', 'with', 'const', 'let', 'enum', 'export', 'import', 'native', '__hasProp', '__extends', '__slice', '__bind', '__indexOf', 'implements', 'interface', 'package', 'private', 'protected', 'public', 'static'];
RESERVED = ['case', 'default', 'function', 'var', 'void', 'with', 'const', 'let', 'enum', 'export', 'import', 'native', 'implements', 'interface', 'package', 'private', 'protected', 'public', 'static'];
STRICT_PROSCRIBED = ['arguments', 'eval', 'yield*'];

View File

@ -1,8 +1,8 @@
// Generated by CoffeeScript 1.8.0
(function() {
var Access, Arr, Assign, Base, Block, Call, Class, Code, CodeFragment, Comment, Existence, Expansion, Extends, For, HEXNUM, IDENTIFIER, IDENTIFIER_STR, IS_REGEX, IS_STRING, If, In, Index, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, Literal, METHOD_DEF, NEGATE, NO, NUMBER, Obj, Op, Param, Parens, RESERVED, Range, Return, SIMPLENUM, STRICT_PROSCRIBED, Scope, Slice, Splat, Switch, TAB, THIS, Throw, Try, UTILITIES, Value, While, YES, addLocationDataFn, compact, del, ends, extend, flatten, fragmentsToText, isLiteralArguments, isLiteralThis, last, locationDataToString, merge, multident, parseNum, some, starts, throwSyntaxError, unfoldSoak, utility, _ref, _ref1,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
__hasProp = {}.hasOwnProperty,
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
__slice = [].slice;
@ -417,7 +417,7 @@
o.indent = o.bare ? '' : TAB;
o.level = LEVEL_TOP;
this.spaced = true;
o.scope = new Scope(null, this, null);
o.scope = new Scope(null, this, null, o.referencedVars);
_ref2 = o.locals || [];
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
name = _ref2[_i];
@ -517,8 +517,8 @@
exports.Literal = Literal = (function(_super) {
__extends(Literal, _super);
function Literal(value) {
this.value = value;
function Literal(_at_value) {
this.value = _at_value;
}
Literal.prototype.makeReturn = function() {
@ -617,8 +617,8 @@
return [this.makeCode(this.val)];
};
function Bool(val) {
this.val = val;
function Bool(_at_val) {
this.val = _at_val;
}
return Bool;
@ -628,8 +628,8 @@
exports.Return = Return = (function(_super) {
__extends(Return, _super);
function Return(expression) {
this.expression = expression;
function Return(_at_expression) {
this.expression = _at_expression;
}
Return.prototype.children = ['expression'];
@ -850,8 +850,8 @@
exports.Comment = Comment = (function(_super) {
__extends(Comment, _super);
function Comment(comment) {
this.comment = comment;
function Comment(_at_comment) {
this.comment = _at_comment;
}
Comment.prototype.isStatement = YES;
@ -875,9 +875,9 @@
exports.Call = Call = (function(_super) {
__extends(Call, _super);
function Call(variable, args, soak) {
this.args = args != null ? args : [];
this.soak = soak;
function Call(variable, _at_args, _at_soak) {
this.args = _at_args != null ? _at_args : [];
this.soak = _at_soak;
this.isNew = false;
this.isSuper = variable === 'super';
this.variable = this.isSuper ? null : variable;
@ -1046,9 +1046,9 @@
exports.Extends = Extends = (function(_super) {
__extends(Extends, _super);
function Extends(child, parent) {
this.child = child;
this.parent = parent;
function Extends(_at_child, _at_parent) {
this.child = _at_child;
this.parent = _at_parent;
}
Extends.prototype.children = ['child', 'parent'];
@ -1064,8 +1064,8 @@
exports.Access = Access = (function(_super) {
__extends(Access, _super);
function Access(name, tag) {
this.name = name;
function Access(_at_name, tag) {
this.name = _at_name;
this.name.asKey = true;
this.soak = tag === 'soak';
}
@ -1093,8 +1093,8 @@
exports.Index = Index = (function(_super) {
__extends(Index, _super);
function Index(index) {
this.index = index;
function Index(_at_index) {
this.index = _at_index;
}
Index.prototype.children = ['index'];
@ -1116,9 +1116,9 @@
Range.prototype.children = ['from', 'to'];
function Range(from, to, tag) {
this.from = from;
this.to = to;
function Range(_at_from, _at_to, tag) {
this.from = _at_from;
this.to = _at_to;
this.exclusive = tag === 'exclusive';
this.equals = this.exclusive ? '' : '=';
}
@ -1214,8 +1214,8 @@
Slice.prototype.children = ['range'];
function Slice(range) {
this.range = range;
function Slice(_at_range) {
this.range = _at_range;
Slice.__super__.constructor.call(this);
}
@ -1240,8 +1240,8 @@
exports.Obj = Obj = (function(_super) {
__extends(Obj, _super);
function Obj(props, generated) {
this.generated = generated != null ? generated : false;
function Obj(props, _at_generated) {
this.generated = _at_generated != null ? _at_generated : false;
this.objects = this.properties = props || [];
}
@ -1379,10 +1379,10 @@
exports.Class = Class = (function(_super) {
__extends(Class, _super);
function Class(variable, parent, body) {
this.variable = variable;
this.parent = parent;
this.body = body != null ? body : new Block;
function Class(_at_variable, _at_parent, _at_body) {
this.variable = _at_variable;
this.parent = _at_parent;
this.body = _at_body != null ? _at_body : new Block;
this.boundFuncs = [];
this.body.classBody = true;
}
@ -1566,11 +1566,11 @@
exports.Assign = Assign = (function(_super) {
__extends(Assign, _super);
function Assign(variable, value, context, options) {
function Assign(_at_variable, _at_value, _at_context, options) {
var forbidden, name, _ref2;
this.variable = variable;
this.value = value;
this.context = context;
this.variable = _at_variable;
this.value = _at_value;
this.context = _at_context;
this.param = options && options.param;
this.subpattern = options && options.subpattern;
forbidden = (_ref2 = (name = this.variable.unwrapAll().value), __indexOf.call(STRICT_PROSCRIBED, _ref2) >= 0);
@ -1875,14 +1875,10 @@
_ref5 = this.params;
for (_k = 0, _len2 = _ref5.length; _k < _len2; _k++) {
p = _ref5[_k].name;
if (!(!(param instanceof Expansion))) {
continue;
}
if (p["this"]) {
p = p.properties[0].name;
}
if (p.value) {
o.scope.add(p.value, 'var', true);
if (!(param instanceof Expansion)) {
if (p.value) {
o.scope.add(p.value, 'var', true);
}
}
}
splats = new Assign(new Value(new Arr((function() {
@ -1935,7 +1931,7 @@
uniqs = [];
this.eachParamName(function(name, node) {
if (__indexOf.call(uniqs, name) >= 0) {
node.error("multiple parameters named '" + name + "'");
node.error("multiple parameters named " + name);
}
return uniqs.push(name);
});
@ -1997,11 +1993,11 @@
exports.Param = Param = (function(_super) {
__extends(Param, _super);
function Param(name, value, splat) {
var _ref2;
this.name = name;
this.value = value;
this.splat = splat;
function Param(_at_name, _at_value, _at_splat) {
var name, _ref2;
this.name = _at_name;
this.value = _at_value;
this.splat = _at_splat;
if (_ref2 = (name = this.name.unwrapAll().value), __indexOf.call(STRICT_PROSCRIBED, _ref2) >= 0) {
this.name.error("parameter name \"" + name + "\" is not allowed");
}
@ -2014,16 +2010,14 @@
};
Param.prototype.asReference = function(o) {
var node;
var name, node;
if (this.reference) {
return this.reference;
}
node = this.name;
if (node["this"]) {
node = node.properties[0].name;
if (node.value.reserved) {
node = new Literal(o.scope.freeVariable(node.value));
}
name = "at_" + node.properties[0].name.value;
node = new Literal(o.scope.freeVariable(name));
} else if (node.isComplex()) {
node = new Literal(o.scope.freeVariable('arg'));
}
@ -2045,11 +2039,7 @@
name = this.name;
}
atParam = function(obj) {
var node;
node = obj.properties[0].name;
if (!node.value.reserved) {
return iterator(node.value, node);
}
return iterator("@" + obj.properties[0].name.value, obj);
};
if (name instanceof Literal) {
return iterator(name.value, name);
@ -2199,8 +2189,8 @@
}
};
While.prototype.addBody = function(body) {
this.body = body;
While.prototype.addBody = function(_at_body) {
this.body = _at_body;
return this;
};
@ -2514,9 +2504,9 @@
exports.In = In = (function(_super) {
__extends(In, _super);
function In(object, array) {
this.object = object;
this.array = array;
function In(_at_object, _at_array) {
this.object = _at_object;
this.array = _at_array;
}
In.prototype.children = ['object', 'array'];
@ -2588,11 +2578,11 @@
exports.Try = Try = (function(_super) {
__extends(Try, _super);
function Try(attempt, errorVariable, recovery, ensure) {
this.attempt = attempt;
this.errorVariable = errorVariable;
this.recovery = recovery;
this.ensure = ensure;
function Try(_at_attempt, _at_errorVariable, _at_recovery, _at_ensure) {
this.attempt = _at_attempt;
this.errorVariable = _at_errorVariable;
this.recovery = _at_recovery;
this.ensure = _at_ensure;
}
Try.prototype.children = ['attempt', 'recovery', 'ensure'];
@ -2630,8 +2620,8 @@
exports.Throw = Throw = (function(_super) {
__extends(Throw, _super);
function Throw(expression) {
this.expression = expression;
function Throw(_at_expression) {
this.expression = _at_expression;
}
Throw.prototype.children = ['expression'];
@ -2653,8 +2643,8 @@
exports.Existence = Existence = (function(_super) {
__extends(Existence, _super);
function Existence(expression) {
this.expression = expression;
function Existence(_at_expression) {
this.expression = _at_expression;
}
Existence.prototype.children = ['expression'];
@ -2681,8 +2671,8 @@
exports.Parens = Parens = (function(_super) {
__extends(Parens, _super);
function Parens(body) {
this.body = body;
function Parens(_at_body) {
this.body = _at_body;
}
Parens.prototype.children = ['body'];
@ -2894,10 +2884,10 @@
exports.Switch = Switch = (function(_super) {
__extends(Switch, _super);
function Switch(subject, cases, otherwise) {
this.subject = subject;
this.cases = cases;
this.otherwise = otherwise;
function Switch(_at_subject, _at_cases, _at_otherwise) {
this.subject = _at_subject;
this.cases = _at_cases;
this.otherwise = _at_otherwise;
}
Switch.prototype.children = ['subject', 'cases', 'otherwise'];
@ -2979,8 +2969,8 @@
exports.If = If = (function(_super) {
__extends(If, _super);
function If(condition, body, options) {
this.body = body;
function If(condition, _at_body, options) {
this.body = _at_body;
if (options == null) {
options = {};
}
@ -3155,9 +3145,13 @@
utility = function(name) {
var ref;
ref = "__" + name;
Scope.root.assign(ref, UTILITIES[name]());
return ref;
if (name in Scope.root.utilities) {
return Scope.root.utilities[name];
} else {
ref = Scope.root.freeVariable("_" + name);
Scope.root.assign(ref, UTILITIES[name]());
return Scope.root.utilities[name] = ref;
}
};
multident = function(code, tab) {

View File

@ -5,8 +5,8 @@
repeat = require('./helpers').repeat;
exports.OptionParser = OptionParser = (function() {
function OptionParser(rules, banner) {
this.banner = banner;
function OptionParser(rules, _at_banner) {
this.banner = _at_banner;
this.rules = buildRules(rules);
}

View File

@ -17,8 +17,8 @@
exports.Rewriter = (function() {
function Rewriter() {}
Rewriter.prototype.rewrite = function(tokens) {
this.tokens = tokens;
Rewriter.prototype.rewrite = function(_at_tokens) {
this.tokens = _at_tokens;
this.removeLeadingNewlines();
this.closeOpenCalls();
this.closeOpenIndexes();

View File

@ -1,16 +1,18 @@
// Generated by CoffeeScript 1.8.0
(function() {
var Scope, extend, last, _ref;
var Scope, extend, last, _ref,
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
_ref = require('./helpers'), extend = _ref.extend, last = _ref.last;
exports.Scope = Scope = (function() {
Scope.root = null;
function Scope(parent, expressions, method) {
this.parent = parent;
this.expressions = expressions;
this.method = method;
function Scope(_at_parent, _at_expressions, _at_method, _at_referencedVars) {
this.parent = _at_parent;
this.expressions = _at_expressions;
this.method = _at_method;
this.referencedVars = _at_referencedVars;
this.variables = [
{
name: 'arguments',
@ -19,6 +21,7 @@
];
this.positions = {};
if (!this.parent) {
this.utilities = {};
Scope.root = this;
}
}
@ -91,7 +94,11 @@
reserve = true;
}
index = 0;
while (this.check((temp = this.temporary(name, index)))) {
while (true) {
temp = this.temporary(name, index);
if (!(this.check(temp) || __indexOf.call(Scope.root.referencedVars, temp) >= 0)) {
break;
}
index++;
}
if (reserve) {

View File

@ -3,8 +3,8 @@
var LineMap, SourceMap;
LineMap = (function() {
function LineMap(line) {
this.line = line;
function LineMap(_at_line) {
this.line = _at_line;
this.columns = [];
}

View File

@ -44,7 +44,17 @@ exports.compile = compile = withPrettyErrors (code, options) ->
if options.sourceMap
map = new SourceMap
fragments = parser.parse(lexer.tokenize code, options).compileToFragments options
tokens = lexer.tokenize code, options
# Pass a list of referenced variables, so that generated variables won't get
# the same name. Since all generated variables start with an underscore only
# referenced variables also starting with an underscore are passed, as an
# optimization.
options.referencedVars = (
token[1] for token in tokens when token.variable and token[1].charAt(0) is '_'
)
fragments = parser.parse(tokens).compileToFragments options
currentLine = 0
currentLine += 1 if options.header

View File

@ -156,6 +156,7 @@ exports.Lexer = class Lexer
else tag
tagToken = @token tag, id, 0, idLength
tagToken.variable = not forcedIdentifier
if poppedToken
[tagToken[2].first_line, tagToken[2].first_column] =
[poppedToken[2].first_line, poppedToken[2].first_column]
@ -717,9 +718,8 @@ COFFEE_KEYWORDS = COFFEE_KEYWORDS.concat COFFEE_ALIASES
# to avoid having a JavaScript error at runtime.
RESERVED = [
'case', 'default', 'function', 'var', 'void', 'with', 'const', 'let', 'enum'
'export', 'import', 'native', '__hasProp', '__extends', '__slice', '__bind'
'__indexOf', 'implements', 'interface', 'package', 'private', 'protected'
'public', 'static'
'export', 'import', 'native', 'implements', 'interface', 'package', 'private'
'protected', 'public', 'static'
]
STRICT_PROSCRIBED = ['arguments', 'eval', 'yield*']

View File

@ -320,7 +320,7 @@ exports.Block = class Block extends Base
o.indent = if o.bare then '' else TAB
o.level = LEVEL_TOP
@spaced = yes
o.scope = new Scope null, this, null
o.scope = new Scope null, this, null, o.referencedVars
# Mark given local variables in the root scope as parameters so they don't
# end up being declared on this block.
o.scope.parameter name for name in o.locals or []
@ -1361,7 +1361,6 @@ exports.Code = class Code extends Base
o.scope.parameter param.asReference o
for param in @params when param.splat or param instanceof Expansion
for {name: p} in @params when param not instanceof Expansion
if p.this then p = p.properties[0].name
if p.value then o.scope.add p.value, 'var', yes
splats = new Assign new Value(new Arr(p.asReference o for p in @params)),
new Value new Literal 'arguments'
@ -1386,7 +1385,7 @@ exports.Code = class Code extends Base
o.scope.parameter fragmentsToText params[i]
uniqs = []
@eachParamName (name, node) ->
node.error "multiple parameters named '#{name}'" if name in uniqs
node.error "multiple parameters named #{name}" if name in uniqs
uniqs.push name
@body.makeReturn() unless wasEmpty or @noReturn
code = 'function'
@ -1431,9 +1430,8 @@ exports.Param = class Param extends Base
return @reference if @reference
node = @name
if node.this
node = node.properties[0].name
if node.value.reserved
node = new Literal o.scope.freeVariable node.value
name = "at_#{node.properties[0].name.value}"
node = new Literal o.scope.freeVariable name
else if node.isComplex()
node = new Literal o.scope.freeVariable 'arg'
node = new Value node
@ -1451,9 +1449,7 @@ exports.Param = class Param extends Base
# `name` is the name of the parameter and `node` is the AST node corresponding
# to that name.
eachName: (iterator, name = @name)->
atParam = (obj) ->
node = obj.properties[0].name
iterator node.value, node unless node.value.reserved
atParam = (obj) -> iterator "@#{obj.properties[0].name.value}", obj
# * simple literals `foo`
return iterator name.value, name if name instanceof Literal
# * at-params `@foo`
@ -2264,9 +2260,12 @@ IS_REGEX = /^\//
# Helper for ensuring that utility functions are assigned at the top level.
utility = (name) ->
ref = "__#{name}"
Scope.root.assign ref, UTILITIES[name]()
ref
if name of Scope.root.utilities
Scope.root.utilities[name]
else
ref = Scope.root.freeVariable "_#{name}"
Scope.root.assign ref, UTILITIES[name]()
Scope.root.utilities[name] = ref
multident = (code, tab) ->
code = code.replace /\n/g, '$&' + tab

View File

@ -17,13 +17,16 @@ The `root` is the top-level **Scope** object for a given file.
Initialize a scope with its parent, for lookups up the chain,
as well as a reference to the **Block** node it belongs to, which is
where it should declare its variables, and a reference to the function that
it belongs to.
where it should declare its variables, a reference to the function that
it belongs to, and a list of variables referenced in the source code
and therefore should be avoided when generating variables.
constructor: (@parent, @expressions, @method) ->
constructor: (@parent, @expressions, @method, @referencedVars) ->
@variables = [{name: 'arguments', type: 'arguments'}]
@positions = {}
Scope.root = this unless @parent
unless @parent
@utilities = {}
Scope.root = this
Adds a new variable or overrides an existing one.
@ -84,7 +87,10 @@ compiler-generated variable. `_var`, `_var2`, and so on...
freeVariable: (name, reserve=true) ->
index = 0
index++ while @check((temp = @temporary name, index))
loop
temp = @temporary name, index
break unless @check(temp) or temp in Scope.root.referencedVars
index++
@add temp, 'var', yes if reserve
temp

View File

@ -6,6 +6,7 @@
# * Destructuring Assignment
# * Context Property (@) Assignment
# * Existential Assignment (?=)
# * Assignment to variables similar to generated variables
test "context property assignment (using @)", ->
nonce = {}
@ -405,3 +406,54 @@ test "#2181: conditional assignment as a subexpression", ->
false && a or= true
eq false, a
eq false, not a or= true
test "#1500: Assignment to variables similar to generated variables", ->
_len = 0
x = ((_results = null; i) for i in [1, 2, 3])
arrayEq [1, 2, 3], x
eq 0, _len
for x in [1, 2, 3]
f = ->
_i = 0
f()
eq 'undefined', typeof _i
_ref = 2
x = _ref * 2 ? 1
eq x, 4
eq 'undefined', typeof _ref1
x = {}
_base = -> x
_name = -1
_base()[-_name] ?= 2
eq x[1], 2
eq _base(), x
eq _name, -1
f = (@a, _at_a, a) -> [@a, _at_a, a]
arrayEq [1, 2, 3], f.call scope = {}, 1, 2, 3
eq 1, scope.a
doesNotThrow -> CoffeeScript.compile '(@_slice...) ->'
test "Assignment to variables similar to helper functions", ->
f = (__slice...) -> __slice
arrayEq [1, 2, 3], f 1, 2, 3
eq 'undefined', typeof __slice1
class A
class B extends A
__extends = 3
__hasProp = 4
value: 5
method: (__bind, __bind1) => [__bind, __bind1, __extends, __hasProp, @value]
{method} = new B
arrayEq [1, 2, 3, 4, 5], method 1, 2
__modulo = -1 %% 3
eq 2, __modulo
__indexOf = [1, 2, 3]
ok 2 in __indexOf

View File

@ -436,3 +436,19 @@ test "unclosed regexes", ->
a #{""" ""#{if /[/].test "|" then 1 else 0}"" """}
^
'''
test "duplicate function arguments", ->
assertErrorFormat '''
(foo, bar, foo) ->
''', '''
[stdin]:1:12: error: multiple parameters named foo
(foo, bar, foo) ->
^^^
'''
assertErrorFormat '''
(@foo, bar, @foo) ->
''', '''
[stdin]:1:13: error: multiple parameters named @foo
(@foo, bar, @foo) ->
^^^^
'''

View File

@ -116,8 +116,13 @@ test "@-parameters: automatically assign an argument's value to a property of th
((@prop...) ->).call context = {}, 0, nonce, 0
eq nonce, context.prop[1]
# the argument should still be able to be referenced normally
eq nonce, (((@prop) -> prop).call {}, nonce)
# the argument should not be able to be referenced normally
code = '((@prop) -> prop).call {}'
doesNotThrow -> CoffeeScript.compile code
throws (-> CoffeeScript.run code), ReferenceError
code = '((@prop) -> _at_prop).call {}'
doesNotThrow -> CoffeeScript.compile code
throws (-> CoffeeScript.run code), ReferenceError
test "@-parameters and splats with constructors", ->
a = {}
@ -201,6 +206,28 @@ test "arguments vs parameters", ->
f = (g) -> g()
eq 5, f (x) -> 5
test "reserved keyword as parameters", ->
f = (_case, @case) -> [_case, @case]
[a, b] = f(1, 2)
eq 1, a
eq 2, b
f = (@case, _case...) -> [@case, _case...]
[a, b, c] = f(1, 2, 3)
eq 1, a
eq 2, b
eq 3, c
test "reserved keyword at-splat", ->
f = (@case...) -> @case
[a, b] = f(1, 2)
eq 1, a
eq 2, b
test "#1574: Destructuring and a parameter named _arg", ->
f = ({a, b}, _arg, _arg1) -> [a, b, _arg, _arg1]
arrayEq [1, 2, 3, 4], f a: 1, b: 2, 3, 4
test "#1844: bound functions in nested comprehensions causing empty var statements", ->
a = ((=>) for a in [0] for b in [0])
eq 1, a.length

View File

@ -72,19 +72,14 @@ test "duplicate formal parameters are prohibited", ->
# a Param can also be a splat (...) or an assignment (param=value)
# the following function expressions should throw errors
strict '(_,_)->', 'param, param'
strict '(_,@_)->', 'param, @param'
strict '(_,_...)->', 'param, param...'
strict '(@_,_...)->', '@param, param...'
strict '(_,_ = true)->', 'param, param='
strict '(@_,@_)->', 'two @params'
strict '(_,@_ = true)->', 'param, @param='
strict '(@case,@case)->', 'two @reserved'
strict '(_,{_})->', 'param, {param}'
strict '(@_,{_})->', '@param, {param}'
strict '({_,_})->', '{param, param}'
strict '({_,@_})->', '{param, @param}'
strict '(_,[_])->', 'param, [param]'
strict '([_,_])->', '[param, param]'
strict '([_,@_])->', '[param, @param]'
strict '(_,[_]=true)->', 'param, [param]='
strict '(_,[@_,{_}])->', 'param, [@param, {param}]'
strict '(_,[_,{@_}])->', 'param, [param, {@param}]'
@ -95,6 +90,12 @@ test "duplicate formal parameters are prohibited", ->
strict '(0:a,1:a)->', '0:param,1:param'
strict '({0:a,1:a})->', '{0:param,1:param}'
# the following function expressions should **not** throw errors
strictOk '(_,@_)->'
strictOk '(@_,_...)->'
strictOk '(_,@_ = true)->'
strictOk '(@_,{_})->'
strictOk '({_,@_})->'
strictOk '([_,@_])->'
strictOk '({},_arg)->'
strictOk '({},{})->'
strictOk '([]...,_arg)->'
@ -113,7 +114,6 @@ test "`delete` operand restrictions", ->
strict 'a = 1; delete a'
strictOk 'delete a' #noop
strict '(a) -> delete a'
strict '(@a) -> delete a'
strict '(a...) -> delete a'
strict '(a = 1) -> delete a'
strict '([a]) -> delete a'