Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CS2] Throw an error for ambiguous get or set keywords or function calls #4484

Merged
merged 10 commits into from
Apr 9, 2017
65 changes: 43 additions & 22 deletions lib/coffeescript/lexer.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 26 additions & 6 deletions src/lexer.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ exports.Lexer = class Lexer
@token 'DEFAULT', id
return id.length

[..., prev] = @tokens
prev = @prev()

tag =
if colon or prev? and
Expand Down Expand Up @@ -170,6 +170,16 @@ exports.Lexer = class Lexer
isForFrom(prev)
tag = 'FORFROM'
@seenFor = no
# Throw an error on attempts to use `get` or `set` as keywords, or
# what CoffeeScript would normally interpret as calls to functions named
# `get` or `set`, i.e. `get({foo: function () {}})`
else if tag is 'PROPERTY' and prev
if prev.spaced and prev[0] in CALLABLE and /^[gs]et$/.test(prev[1])
@error "'#{prev[1]}' cannot be used as a keyword, or as a function call without parentheses", prev[2]
else
prevprev = @tokens[@tokens.length - 2]
if prev[0] in ['@', 'THIS'] and prevprev and prevprev.spaced and /^[gs]et$/.test(prevprev[1])
@error "'#{prevprev[1]}' cannot be used as a keyword, or as a function call without parentheses", prevprev[2]

if tag is 'IDENTIFIER' and id in RESERVED
@error "reserved word '#{id}'", length: id.length
Expand Down Expand Up @@ -235,10 +245,15 @@ exports.Lexer = class Lexer
[quote] = STRING_START.exec(@chunk) || []
return 0 unless quote

prev = @prev()

# If the preceding token is `from` and this is an import or export statement,
# properly tag the `from`.
if @tokens.length and @value() is 'from' and (@seenImport or @seenExport)
@tokens[@tokens.length - 1][0] = 'FROM'
if prev and @value() is 'from' and (@seenImport or @seenExport)
prev[0] = 'FROM'

if prev and prev.spaced and prev[0] in CALLABLE and /^[gs]et$/.test(prev[1])
@error "'#{prev[1]}' cannot be used as a keyword, or as a function call without parentheses", prev[2]

regex = switch quote
when "'" then STRING_SINGLE
Expand Down Expand Up @@ -318,7 +333,7 @@ exports.Lexer = class Lexer
[regex, body, closed] = match
@validateEscapes body, isRegex: yes, offsetInChunk: 1
index = regex.length
[..., prev] = @tokens
prev = @prev()
if prev
if prev.spaced and prev[0] in CALLABLE
return 0 if not closed or POSSIBLY_DIVISION.test regex
Expand Down Expand Up @@ -443,7 +458,7 @@ exports.Lexer = class Lexer
whitespaceToken: ->
return 0 unless (match = WHITESPACE.exec @chunk) or
(nline = @chunk.charAt(0) is '\n')
[..., prev] = @tokens
prev = @prev()
prev[if match then 'spaced' else 'newLine'] = true if prev
if match then match[0].length else 0

Expand Down Expand Up @@ -471,7 +486,7 @@ exports.Lexer = class Lexer
else
value = @chunk.charAt 0
tag = value
[..., prev] = @tokens
prev = @prev()

if prev and value in ['=', COMPOUND_ASSIGN...]
skipToken = false
Expand Down Expand Up @@ -761,6 +776,11 @@ exports.Lexer = class Lexer
[..., token] = @tokens
token?[1]

# Get the previous token in the token stream.
prev: ->
[..., token] = @tokens
token if token?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think those two lines can be replaced with @tokens[@tokens.length - 1] - if there's no token, it'll be undefined


# Are we in the midst of an unfinished expression?
unfinished: ->
LINE_CONTINUER.test(@chunk) or
Expand Down
2 changes: 1 addition & 1 deletion src/nodes.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -764,7 +764,7 @@ exports.Call = class Call extends Base
constructor: (@variable, @args = [], @soak) ->
super()

@isNew = false
@isNew = no
if @variable instanceof Value and @variable.isNotCallable()
@variable.error "literal is not a function"

Expand Down
Loading