Issue #1364, implicit calls against control structures.

This commit is contained in:
Jeremy Ashkenas
2011-05-15 10:41:41 -04:00
parent b780d707ab
commit cd5c41f351
3 changed files with 56 additions and 13 deletions

View File

@@ -162,7 +162,7 @@
return this.tokens.splice(idx, 0, ['CALL_END', ')', token[2]]);
};
return this.scanTokens(function(token, i, tokens) {
var callObject, current, next, prev, seenSingle, tag, _ref, _ref2, _ref3;
var callObject, current, next, prev, seenControl, seenSingle, tag, _ref, _ref2, _ref3;
tag = token[0];
if (tag === 'CLASS' || tag === 'IF') {
noCall = true;
@@ -170,6 +170,7 @@
_ref = tokens.slice(i - 1, (i + 1 + 1) || 9e9), prev = _ref[0], current = _ref[1], next = _ref[2];
callObject = !noCall && tag === 'INDENT' && next && next.generated && next[0] === '{' && prev && (_ref2 = prev[0], __indexOf.call(IMPLICIT_FUNC, _ref2) >= 0);
seenSingle = false;
seenControl = false;
if (__indexOf.call(LINEBREAKS, tag) >= 0) {
noCall = false;
}
@@ -192,10 +193,13 @@
if (tag === 'IF' || tag === 'ELSE' || tag === '->' || tag === '=>') {
seenSingle = true;
}
if (tag === 'IF' || tag === 'ELSE' || tag === 'SWITCH' || tag === 'TRY') {
seenControl = true;
}
if ((tag === '.' || tag === '?.' || tag === '::') && this.tag(i - 1) === 'OUTDENT') {
return true;
}
return !token.generated && this.tag(i - 1) !== ',' && __indexOf.call(IMPLICIT_END, tag) >= 0 && (tag !== 'INDENT' || (this.tag(i - 2) !== 'CLASS' && (_ref4 = this.tag(i - 1), __indexOf.call(IMPLICIT_BLOCK, _ref4) < 0) && !((post = this.tokens[i + 1]) && post.generated && post[0] === '{')));
return !token.generated && this.tag(i - 1) !== ',' && (__indexOf.call(IMPLICIT_END, tag) >= 0 || (tag === 'INDENT' && !seenControl)) && (tag !== 'INDENT' || (this.tag(i - 2) !== 'CLASS' && (_ref4 = this.tag(i - 1), __indexOf.call(IMPLICIT_BLOCK, _ref4) < 0) && !((post = this.tokens[i + 1]) && post.generated && post[0] === '{')));
}, action);
if (prev[0] === '?') {
prev[0] = 'FUNC_EXIST';
@@ -352,7 +356,7 @@
IMPLICIT_CALL = ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'CLASS', 'IF', 'TRY', 'SWITCH', 'THIS', 'BOOL', 'UNARY', 'SUPER', '@', '->', '=>', '[', '(', '{', '--', '++'];
IMPLICIT_UNSPACED_CALL = ['+', '-'];
IMPLICIT_BLOCK = ['->', '=>', '{', '[', ','];
IMPLICIT_END = ['POST_IF', 'FOR', 'WHILE', 'UNTIL', 'WHEN', 'BY', 'LOOP', 'TERMINATOR', 'INDENT'];
IMPLICIT_END = ['POST_IF', 'FOR', 'WHILE', 'UNTIL', 'WHEN', 'BY', 'LOOP', 'TERMINATOR'];
SINGLE_LINERS = ['ELSE', '->', '=>', 'TRY', 'FINALLY', 'THEN'];
SINGLE_CLOSERS = ['TERMINATOR', 'CATCH', 'FINALLY', 'ELSE', 'OUTDENT', 'LEADING_WHEN'];
LINEBREAKS = ['TERMINATOR', 'INDENT', 'OUTDENT'];

View File

@@ -140,12 +140,13 @@ class exports.Rewriter
tag = token[0]
noCall = yes if tag in ['CLASS', 'IF']
[prev, current, next] = tokens[i - 1 .. i + 1]
callObject = not noCall and tag is 'INDENT' and
next and next.generated and next[0] is '{' and
prev and prev[0] in IMPLICIT_FUNC
seenSingle = no
noCall = no if tag in LINEBREAKS
token.call = yes if prev and not prev.spaced and tag is '?'
callObject = not noCall and tag is 'INDENT' and
next and next.generated and next[0] is '{' and
prev and prev[0] in IMPLICIT_FUNC
seenSingle = no
seenControl = no
noCall = no if tag in LINEBREAKS
token.call = yes if prev and not prev.spaced and tag is '?'
return 1 if token.fromThen
return 1 unless callObject or
prev?.spaced and (prev.call or prev[0] in IMPLICIT_FUNC) and
@@ -154,9 +155,11 @@ class exports.Rewriter
@detectEnd i + 1, (token, i) ->
[tag] = token
return yes if not seenSingle and token.fromThen
seenSingle = yes if tag in ['IF', 'ELSE', '->', '=>']
seenSingle = yes if tag in ['IF', 'ELSE', '->', '=>']
seenControl = yes if tag in ['IF', 'ELSE', 'SWITCH', 'TRY']
return yes if tag in ['.', '?.', '::'] and @tag(i - 1) is 'OUTDENT'
not token.generated and @tag(i - 1) isnt ',' and tag in IMPLICIT_END and
not token.generated and @tag(i - 1) isnt ',' and (tag in IMPLICIT_END or
(tag is 'INDENT' and not seenControl)) and
(tag isnt 'INDENT' or
(@tag(i - 2) isnt 'CLASS' and @tag(i - 1) not in IMPLICIT_BLOCK and
not ((post = @tokens[i + 1]) and post.generated and post[0] is '{')))
@@ -319,7 +322,7 @@ IMPLICIT_UNSPACED_CALL = ['+', '-']
IMPLICIT_BLOCK = ['->', '=>', '{', '[', ',']
# Tokens that always mark the end of an implicit call for single-liners.
IMPLICIT_END = ['POST_IF', 'FOR', 'WHILE', 'UNTIL', 'WHEN', 'BY', 'LOOP', 'TERMINATOR', 'INDENT']
IMPLICIT_END = ['POST_IF', 'FOR', 'WHILE', 'UNTIL', 'WHEN', 'BY', 'LOOP', 'TERMINATOR']
# Single-line flavors of block expressions that have unclosed endings.
# The grammar can't disambiguate them, so we insert the implicit indentation.

View File

@@ -438,8 +438,44 @@ test "don't wrap 'pure' statements in a closure", ->
return item if item is nonce
eq nonce, fn items
#### Unusual `new` Usage
test "usage of `new` is careful about where the invocation parens end up", ->
eq 'object', typeof new try Array
eq 'object', typeof new do -> ->
test "implicit call against control structures", ->
result = null
save = (obj) -> result = obj
save switch id false
when true
'true'
when false
'false'
eq result, 'false'
save if id false
'false'
else
'true'
eq result, 'true'
save unless id false
'true'
else
'false'
eq result, 'true'
save try
doesnt exist
catch error
'caught'
eq result, 'caught'