Skip to content

Commit cebe6b1

Browse files
committed
Properly update location data when setting a call to use new
This is an upstream port of decaffeinate#24 In a case like `new A().b(c)`, the jison structure ends up being different from the resulting AST. To the jison parser, this is the `new` unary operator applied to the expression `A().b(c)`. When the unary operator is applied, the `Call.prototype.newInstance` function traverses into the leftmost function call and sets the `isNew` flag to true, and the `Op` constructor returns the `Call` node so that the call is used in place of the unary operator. However, the code wasn't updating the node location data, so this commit fixes that. It's sort of hard to get the location data in `newInstance`, so we set a flag on every affected node in `newInstance` and override `updateLocationDataIfMissing` (which is called with the location data after the fact) so that it updates just the starting position.
1 parent dd72594 commit cebe6b1

File tree

3 files changed

+73
-0
lines changed

3 files changed

+73
-0
lines changed

lib/coffee-script/nodes.js

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/nodes.coffee

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,13 +633,29 @@ exports.Call = class Call extends Base
633633

634634
children: ['variable', 'args']
635635

636+
# When setting the location, we sometimes need to update the start location to
637+
# account for a newly-discovered `new` operator to the left of us. This
638+
# expands the range on the left, but not the right.
639+
updateLocationDataIfMissing: (locationData) ->
640+
if @locationData and @needsUpdatedStartLocation
641+
@locationData.first_line = locationData.first_line
642+
@locationData.first_column = locationData.first_column
643+
base = @variable?.base or @variable
644+
if base.needsUpdatedStartLocation
645+
@variable.locationData.first_line = locationData.first_line
646+
@variable.locationData.first_column = locationData.first_column
647+
base.updateLocationDataIfMissing locationData
648+
delete @needsUpdatedStartLocation
649+
super
650+
636651
# Tag this invocation as creating a new instance.
637652
newInstance: ->
638653
base = @variable?.base or @variable
639654
if base instanceof Call and not base.isNew
640655
base.newInstance()
641656
else
642657
@isNew = true
658+
@needsUpdatedStartLocation = true
643659
this
644660

645661
# Soaked chained invocations unfold into if/else ternary structures.

test/parser.coffee

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,43 @@ test "operator precedence for binary ? operator", ->
3838
eq expression.second.first.base.value, 'b'
3939
eq expression.second.operator, '&&'
4040
eq expression.second.second.base.value, 'c'
41+
42+
test "new calls have a range including the new", ->
43+
source = '''
44+
a = new B().c(d)
45+
'''
46+
block = CoffeeScript.nodes source
47+
48+
assertColumnRange = (node, firstColumn, lastColumn) ->
49+
eq node.locationData.first_line, 0
50+
eq node.locationData.first_column, firstColumn
51+
eq node.locationData.last_line, 0
52+
eq node.locationData.last_column, lastColumn
53+
54+
[assign] = block.expressions
55+
outerCall = assign.value
56+
innerValue = outerCall.variable
57+
innerCall = innerValue.base
58+
59+
assertColumnRange assign, 0, 15
60+
assertColumnRange outerCall, 4, 15
61+
assertColumnRange innerValue, 4, 12
62+
assertColumnRange innerCall, 4, 10
63+
64+
test "location data is properly set for nested `new`", ->
65+
source = '''
66+
new new A()()
67+
'''
68+
block = CoffeeScript.nodes source
69+
70+
assertColumnRange = (node, firstColumn, lastColumn) ->
71+
eq node.locationData.first_line, 0
72+
eq node.locationData.first_column, firstColumn
73+
eq node.locationData.last_line, 0
74+
eq node.locationData.last_column, lastColumn
75+
76+
[outerCall] = block.expressions
77+
innerCall = outerCall.variable
78+
79+
assertColumnRange outerCall, 0, 12
80+
assertColumnRange innerCall, 4, 10

0 commit comments

Comments
 (0)