mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00
* support new.target * check token type
This commit is contained in:
parent
c4245e50c2
commit
6225627579
6 changed files with 68 additions and 1 deletions
|
@ -202,6 +202,8 @@
|
|||
} else if (tag === 'PROPERTY' && prev) {
|
||||
if (prev.spaced && (ref7 = prev[0], indexOf.call(CALLABLE, ref7) >= 0) && /^[gs]et$/.test(prev[1]) && this.tokens.length > 1 && ((ref8 = this.tokens[this.tokens.length - 2][0]) !== '.' && ref8 !== '?.' && ref8 !== '@')) {
|
||||
this.error(`'${prev[1]}' cannot be used as a keyword, or as a function call without parentheses`, prev[2]);
|
||||
} else if (prev[0] === '.' && this.tokens.length > 1 && (prevprev = this.tokens[this.tokens.length - 2])[0] === 'UNARY' && prevprev[1] === 'new') {
|
||||
prevprev[0] = 'IDENTIFIER';
|
||||
} else if (this.tokens.length > 2) {
|
||||
prevprev = this.tokens[this.tokens.length - 2];
|
||||
if (((ref9 = prev[0]) === '@' || ref9 === 'THIS') && prevprev && prevprev.spaced && /^[gs]et$/.test(prevprev[1]) && ((ref10 = this.tokens[this.tokens.length - 3][0]) !== '.' && ref10 !== '?.' && ref10 !== '@')) {
|
||||
|
|
|
@ -1445,6 +1445,7 @@
|
|||
// evaluate anything twice when building the soak chain.
|
||||
compileNode(o) {
|
||||
var fragments, j, len1, prop, props;
|
||||
this.checkNewTarget(o);
|
||||
this.base.front = this.front;
|
||||
props = this.properties;
|
||||
if (props.length && (this.base.cached != null)) {
|
||||
|
@ -1468,6 +1469,19 @@
|
|||
return fragments;
|
||||
}
|
||||
|
||||
checkNewTarget(o) {
|
||||
if (!(this.base instanceof IdentifierLiteral && this.base.value === 'new' && this.properties.length)) {
|
||||
return;
|
||||
}
|
||||
if (this.properties[0] instanceof Access && this.properties[0].name.value === 'target') {
|
||||
if (o.scope.parent == null) {
|
||||
return this.error("new.target can only occur inside functions");
|
||||
}
|
||||
} else {
|
||||
return this.error("the only valid meta property for new is new.target");
|
||||
}
|
||||
}
|
||||
|
||||
// Unfold a soak into an `If`: `a?.b` -> `a.b if a?`
|
||||
unfoldSoak(o) {
|
||||
return this.unfoldedSoak != null ? this.unfoldedSoak : this.unfoldedSoak = (() => {
|
||||
|
|
|
@ -196,6 +196,8 @@ exports.Lexer = class Lexer
|
|||
@tokens.length > 1 and @tokens[@tokens.length - 2][0] not in ['.', '?.', '@']
|
||||
@error "'#{prev[1]}' cannot be used as a keyword, or as a function call
|
||||
without parentheses", prev[2]
|
||||
else if prev[0] is '.' and @tokens.length > 1 and (prevprev = @tokens[@tokens.length - 2])[0] is 'UNARY' and prevprev[1] is 'new'
|
||||
prevprev[0] = 'IDENTIFIER'
|
||||
else if @tokens.length > 2
|
||||
prevprev = @tokens[@tokens.length - 2]
|
||||
if prev[0] in ['@', 'THIS'] and prevprev and prevprev.spaced and
|
||||
|
|
|
@ -965,6 +965,7 @@ exports.Value = class Value extends Base
|
|||
# operators `?.` interspersed. Then we have to take care not to accidentally
|
||||
# evaluate anything twice when building the soak chain.
|
||||
compileNode: (o) ->
|
||||
@checkNewTarget o
|
||||
@base.front = @front
|
||||
props = @properties
|
||||
if props.length and @base.cached?
|
||||
|
@ -984,6 +985,14 @@ exports.Value = class Value extends Base
|
|||
|
||||
fragments
|
||||
|
||||
checkNewTarget: (o) ->
|
||||
return unless @base instanceof IdentifierLiteral and @base.value is 'new' and @properties.length
|
||||
if @properties[0] instanceof Access and @properties[0].name.value is 'target'
|
||||
unless o.scope.parent?
|
||||
@error "new.target can only occur inside functions"
|
||||
else
|
||||
@error "the only valid meta property for new is new.target"
|
||||
|
||||
# Unfold a soak into an `If`: `a?.b` -> `a.b if a?`
|
||||
unfoldSoak: (o) ->
|
||||
@unfoldedSoak ?= do =>
|
||||
|
|
|
@ -1899,4 +1899,26 @@ test "#4868: Incorrect ‘Can’t call super with @params’ error", ->
|
|||
super class then constructor: (@a) -> @a = 3
|
||||
|
||||
d = new (new D).c
|
||||
eq 3, d.a
|
||||
eq 3, d.a
|
||||
|
||||
test "#4609: Support new.target", ->
|
||||
class A
|
||||
constructor: ->
|
||||
@calledAs = new.target.name
|
||||
|
||||
class B extends A
|
||||
|
||||
b = new B
|
||||
eq b.calledAs, 'B'
|
||||
|
||||
newTarget = null
|
||||
Foo = ->
|
||||
newTarget = !!new.target
|
||||
|
||||
Foo()
|
||||
eq newTarget, no
|
||||
|
||||
newTarget = null
|
||||
|
||||
new Foo()
|
||||
eq newTarget, yes
|
||||
|
|
|
@ -1907,3 +1907,21 @@ test "#3933: prevent implicit calls when cotrol flow is missing `THEN`", ->
|
|||
when a ->
|
||||
^^
|
||||
'''
|
||||
|
||||
test "`new.target` outside of a function", ->
|
||||
assertErrorFormat '''
|
||||
new.target
|
||||
''', '''
|
||||
[stdin]:1:1: error: new.target can only occur inside functions
|
||||
new.target
|
||||
^^^^^^^^^^
|
||||
'''
|
||||
|
||||
test "`new.target` is only allowed meta property", ->
|
||||
assertErrorFormat '''
|
||||
-> new.something
|
||||
''', '''
|
||||
[stdin]:1:4: error: the only valid meta property for new is new.target
|
||||
-> new.something
|
||||
^^^^^^^^^^^^^
|
||||
'''
|
||||
|
|
Loading…
Reference in a new issue