1
0
Fork 0
mirror of https://github.com/jashkenas/coffeescript.git synced 2022-11-09 12:23:24 -05:00

Fix import/export list bug with aliased keywords (#4744)

This commit is contained in:
geebo 2017-10-12 11:47:12 -07:00 committed by Geoffrey Booth
parent 4d4e47bfb2
commit 063c2d1f56
4 changed files with 58 additions and 31 deletions

View file

@ -111,7 +111,7 @@
// referenced as property names here, so you can still do `jQuery.is()` even
// though `is` means `===` otherwise.
identifierToken() {
var alias, colon, colonOffset, colonToken, id, idLength, inCSXTag, input, match, poppedToken, prev, prevprev, ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, regExSuper, regex, sup, tag, tagToken;
var alias, colon, colonOffset, colonToken, id, idLength, inCSXTag, input, match, poppedToken, prev, prevprev, ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, ref8, regExSuper, regex, sup, tag, tagToken;
inCSXTag = this.atCSXTag();
regex = inCSXTag ? CSX_ATTRIBUTE : IDENTIFIER;
if (!(match = regex.exec(this.chunk))) {
@ -132,19 +132,28 @@
if (id === 'as' && this.seenImport) {
if (this.value() === '*') {
this.tokens[this.tokens.length - 1][0] = 'IMPORT_ALL';
} else if (ref = this.value(), indexOf.call(COFFEE_KEYWORDS, ref) >= 0) {
this.tokens[this.tokens.length - 1][0] = 'IDENTIFIER';
} else if (ref = this.value(true), indexOf.call(COFFEE_KEYWORDS, ref) >= 0) {
prev = this.prev();
[prev[0], prev[1]] = ['IDENTIFIER', this.value(true)];
}
if ((ref1 = this.tag()) === 'DEFAULT' || ref1 === 'IMPORT_ALL' || ref1 === 'IDENTIFIER') {
this.token('AS', id);
return id.length;
}
}
if (id === 'as' && this.seenExport && ((ref2 = this.tag()) === 'IDENTIFIER' || ref2 === 'DEFAULT')) {
this.token('AS', id);
return id.length;
if (id === 'as' && this.seenExport) {
if ((ref2 = this.tag()) === 'IDENTIFIER' || ref2 === 'DEFAULT') {
this.token('AS', id);
return id.length;
}
if (ref3 = this.value(true), indexOf.call(COFFEE_KEYWORDS, ref3) >= 0) {
prev = this.prev();
[prev[0], prev[1]] = ['IDENTIFIER', this.value(true)];
this.token('AS', id);
return id.length;
}
}
if (id === 'default' && this.seenExport && ((ref3 = this.tag()) === 'EXPORT' || ref3 === 'AS')) {
if (id === 'default' && this.seenExport && ((ref4 = this.tag()) === 'EXPORT' || ref4 === 'AS')) {
this.token('DEFAULT', id);
return id.length;
}
@ -156,10 +165,10 @@
return sup.length + 3;
}
prev = this.prev();
tag = colon || (prev != null) && (((ref4 = prev[0]) === '.' || ref4 === '?.' || ref4 === '::' || ref4 === '?::') || !prev.spaced && prev[0] === '@') ? 'PROPERTY' : 'IDENTIFIER';
tag = colon || (prev != null) && (((ref5 = prev[0]) === '.' || ref5 === '?.' || ref5 === '::' || ref5 === '?::') || !prev.spaced && prev[0] === '@') ? 'PROPERTY' : 'IDENTIFIER';
if (tag === 'IDENTIFIER' && (indexOf.call(JS_KEYWORDS, id) >= 0 || indexOf.call(COFFEE_KEYWORDS, id) >= 0) && !(this.exportSpecifierList && indexOf.call(COFFEE_KEYWORDS, id) >= 0)) {
tag = id.toUpperCase();
if (tag === 'WHEN' && (ref5 = this.tag(), indexOf.call(LINE_BREAK, ref5) >= 0)) {
if (tag === 'WHEN' && (ref6 = this.tag(), indexOf.call(LINE_BREAK, ref6) >= 0)) {
tag = 'LEADING_WHEN';
} else if (tag === 'FOR') {
this.seenFor = true;
@ -190,11 +199,11 @@
// what CoffeeScript would normally interpret as calls to functions named
// `get` or `set`, i.e. `get({foo: function () {}})`.
} else if (tag === 'PROPERTY' && prev) {
if (prev.spaced && (ref6 = prev[0], indexOf.call(CALLABLE, ref6) >= 0) && /^[gs]et$/.test(prev[1]) && this.tokens[this.tokens.length - 2][0] !== '.') {
if (prev.spaced && (ref7 = prev[0], indexOf.call(CALLABLE, ref7) >= 0) && /^[gs]et$/.test(prev[1]) && this.tokens[this.tokens.length - 2][0] !== '.') {
this.error(`'${prev[1]}' cannot be used as a keyword, or as a function call without parentheses`, prev[2]);
} else {
prevprev = this.tokens[this.tokens.length - 2];
if (((ref7 = prev[0]) === '@' || ref7 === 'THIS') && prevprev && prevprev.spaced && /^[gs]et$/.test(prevprev[1]) && this.tokens[this.tokens.length - 3][0] !== '.') {
if (((ref8 = prev[0]) === '@' || ref8 === 'THIS') && prevprev && prevprev.spaced && /^[gs]et$/.test(prevprev[1]) && this.tokens[this.tokens.length - 3][0] !== '.') {
this.error(`'${prevprev[1]}' cannot be used as a keyword, or as a function call without parentheses`, prevprev[2]);
}
}
@ -204,7 +213,7 @@
length: id.length
});
}
if (tag !== 'PROPERTY') {
if (!(tag === 'PROPERTY' || this.exportSpecifierList)) {
if (indexOf.call(COFFEE_ALIASES, id) >= 0) {
alias = id;
id = COFFEE_ALIAS_MAP[id];
@ -1275,10 +1284,14 @@
}
// Peek at the last value in the token stream.
value() {
var ref, token;
value(useOrigin = false) {
var ref, ref1, token;
ref = this.tokens, token = ref[ref.length - 1];
return token != null ? token[1] : void 0;
if (useOrigin && ((token != null ? token.origin : void 0) != null)) {
return (ref1 = token.origin) != null ? ref1[1] : void 0;
} else {
return token != null ? token[1] : void 0;
}
}
// Get the previous token in the token stream.

2
package-lock.json generated
View file

@ -1,6 +1,6 @@
{
"name": "coffeescript",
"version": "2.0.0",
"version": "2.0.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View file

@ -116,7 +116,6 @@ exports.Lexer = class Lexer
# Preserve length of id for location data
idLength = id.length
poppedToken = undefined
if id is 'own' and @tag() is 'FOR'
@token 'OWN', id
return id.length
@ -126,14 +125,21 @@ exports.Lexer = class Lexer
if id is 'as' and @seenImport
if @value() is '*'
@tokens[@tokens.length - 1][0] = 'IMPORT_ALL'
else if @value() in COFFEE_KEYWORDS
@tokens[@tokens.length - 1][0] = 'IDENTIFIER'
else if @value(yes) in COFFEE_KEYWORDS
prev = @prev()
[prev[0], prev[1]] = ['IDENTIFIER', @value(yes)]
if @tag() in ['DEFAULT', 'IMPORT_ALL', 'IDENTIFIER']
@token 'AS', id
return id.length
if id is 'as' and @seenExport and @tag() in ['IDENTIFIER', 'DEFAULT']
@token 'AS', id
return id.length
if id is 'as' and @seenExport
if @tag() in ['IDENTIFIER', 'DEFAULT']
@token 'AS', id
return id.length
if @value(yes) in COFFEE_KEYWORDS
prev = @prev()
[prev[0], prev[1]] = ['IDENTIFIER', @value(yes)]
@token 'AS', id
return id.length
if id is 'default' and @seenExport and @tag() in ['EXPORT', 'AS']
@token 'DEFAULT', id
return id.length
@ -197,7 +203,7 @@ exports.Lexer = class Lexer
if tag is 'IDENTIFIER' and id in RESERVED
@error "reserved word '#{id}'", length: id.length
unless tag is 'PROPERTY'
unless tag is 'PROPERTY' or @exportSpecifierList
if id in COFFEE_ALIASES
alias = id
id = COFFEE_ALIAS_MAP[id]
@ -965,9 +971,12 @@ exports.Lexer = class Lexer
token?[0]
# Peek at the last value in the token stream.
value: ->
value: (useOrigin = no) ->
[..., token] = @tokens
token?[1]
if useOrigin and token?.origin?
token.origin?[1]
else
token?[1]
# Get the previous token in the token stream.
prev: ->

View file

@ -171,6 +171,7 @@ test "multiline simple import", ->
bar as baz
} from 'lib';"""
test "multiline complex import", ->
eqJS """
import foo, {
@ -497,18 +498,21 @@ test "export as aliases members imported from another module", ->
} from 'lib';"""
test "export list can contain CoffeeScript keywords", ->
eqJS "export { unless } from 'lib'",
eqJS "export { unless, and } from 'lib'",
"""
export {
unless
unless,
and
} from 'lib';"""
test "export list can contain CoffeeScript keywords when aliasing", ->
eqJS "export { when as bar, baz as unless } from 'lib'",
eqJS "export { when as bar, baz as unless, and as foo, booze as not } from 'lib'",
"""
export {
when as bar,
baz as unless
baz as unless,
and as foo,
booze as not
} from 'lib';"""
@ -602,11 +606,12 @@ test "`as` can be used as an alias name", ->
test "CoffeeScript keywords can be used as imported names in import lists", ->
eqJS """
import { unless as bar } from 'lib'
import { unless as bar, and as computedAnd } from 'lib'
bar.barMethod()""",
"""
import {
unless as bar
unless as bar,
and as computedAnd
} from 'lib';
bar.barMethod();"""