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

* Throw an error for ambiguous `get` or `set` function calls or ES5 getter/setter keywords, to warn the user to use parentheses if they intend a function call (or to inform them that `get` or `set` cannot be used as a keyword)

* Code golf

* Catch get or set keyword before static method

* DRY up getting the previous token

* Throw an error if get or set are used as keywords before what looks like a function or method with an interpolated/dynamic name

* Allow `get` or `set` parentheses-less function calls when first argument is a string without a colon (so a plain string, not a property accessor)

* Revert "Allow `get` or `set` parentheses-less function calls when first argument is a string without a colon (so a plain string, not a property accessor)"

This reverts commit 2d1addf5a4.

* Optimization

* No longer throw an error on `get` or `set` function calls to objects with dynamic property names (introduces a way to circumvent our check for trying to avoid the `get` or `set` keywords, but not worth the complications for this tiny edge case)
This commit is contained in:
Geoffrey Booth
2017-04-08 21:59:09 -07:00
committed by GitHub
parent 76945ab458
commit 8292d25d29
5 changed files with 234 additions and 27 deletions

View File

@@ -132,7 +132,7 @@ exports.Lexer = class Lexer
@token 'DEFAULT', id
return id.length
[..., prev] = @tokens
prev = @prev()
tag =
if colon or prev? and
@@ -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
@@ -237,8 +247,9 @@ exports.Lexer = class Lexer
# 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'
prev = @prev()
if prev and @value() is 'from' and (@seenImport or @seenExport)
prev[0] = 'FROM'
regex = switch quote
when "'" then STRING_SINGLE
@@ -318,7 +329,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
@@ -440,7 +451,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
@@ -468,7 +479,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
@@ -758,6 +769,10 @@ exports.Lexer = class Lexer
[..., token] = @tokens
token?[1]
# Get the previous token in the token stream.
prev: ->
@tokens[@tokens.length - 1]
# Are we in the midst of an unfinished expression?
unfinished: ->
LINE_CONTINUER.test(@chunk) or

View File

@@ -775,7 +775,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"