diff --git a/lib/coffee-script/nodes.js b/lib/coffee-script/nodes.js index a4d09cc5..714188e6 100644 --- a/lib/coffee-script/nodes.js +++ b/lib/coffee-script/nodes.js @@ -1024,6 +1024,22 @@ Call.prototype.children = ['variable', 'args']; + Call.prototype.updateLocationDataIfMissing = function(locationData) { + var base, ref3; + if (this.locationData && this.needsUpdatedStartLocation) { + this.locationData.first_line = locationData.first_line; + this.locationData.first_column = locationData.first_column; + base = ((ref3 = this.variable) != null ? ref3.base : void 0) || this.variable; + if (base.needsUpdatedStartLocation) { + this.variable.locationData.first_line = locationData.first_line; + this.variable.locationData.first_column = locationData.first_column; + base.updateLocationDataIfMissing(locationData); + } + delete this.needsUpdatedStartLocation; + } + return Call.__super__.updateLocationDataIfMissing.apply(this, arguments); + }; + Call.prototype.newInstance = function() { var base, ref3; base = ((ref3 = this.variable) != null ? ref3.base : void 0) || this.variable; @@ -1032,6 +1048,7 @@ } else { this.isNew = true; } + this.needsUpdatedStartLocation = true; return this; }; diff --git a/src/nodes.coffee b/src/nodes.coffee index adb1a9a6..633c11cf 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -633,6 +633,21 @@ exports.Call = class Call extends Base children: ['variable', 'args'] + # When setting the location, we sometimes need to update the start location to + # account for a newly-discovered `new` operator to the left of us. This + # expands the range on the left, but not the right. + updateLocationDataIfMissing: (locationData) -> + if @locationData and @needsUpdatedStartLocation + @locationData.first_line = locationData.first_line + @locationData.first_column = locationData.first_column + base = @variable?.base or @variable + if base.needsUpdatedStartLocation + @variable.locationData.first_line = locationData.first_line + @variable.locationData.first_column = locationData.first_column + base.updateLocationDataIfMissing locationData + delete @needsUpdatedStartLocation + super + # Tag this invocation as creating a new instance. newInstance: -> base = @variable?.base or @variable @@ -640,6 +655,7 @@ exports.Call = class Call extends Base base.newInstance() else @isNew = true + @needsUpdatedStartLocation = true this # Soaked chained invocations unfold into if/else ternary structures. diff --git a/test/parser.coffee b/test/parser.coffee index 33fbff1e..fce29393 100644 --- a/test/parser.coffee +++ b/test/parser.coffee @@ -38,3 +38,43 @@ test "operator precedence for binary ? operator", -> eq expression.second.first.base.value, 'b' eq expression.second.operator, '&&' eq expression.second.second.base.value, 'c' + +test "new calls have a range including the new", -> + source = ''' + a = new B().c(d) + ''' + block = CoffeeScript.nodes source + + assertColumnRange = (node, firstColumn, lastColumn) -> + eq node.locationData.first_line, 0 + eq node.locationData.first_column, firstColumn + eq node.locationData.last_line, 0 + eq node.locationData.last_column, lastColumn + + [assign] = block.expressions + outerCall = assign.value + innerValue = outerCall.variable + innerCall = innerValue.base + + assertColumnRange assign, 0, 15 + assertColumnRange outerCall, 4, 15 + assertColumnRange innerValue, 4, 12 + assertColumnRange innerCall, 4, 10 + +test "location data is properly set for nested `new`", -> + source = ''' + new new A()() + ''' + block = CoffeeScript.nodes source + + assertColumnRange = (node, firstColumn, lastColumn) -> + eq node.locationData.first_line, 0 + eq node.locationData.first_column, firstColumn + eq node.locationData.last_line, 0 + eq node.locationData.last_column, lastColumn + + [outerCall] = block.expressions + innerCall = outerCall.variable + + assertColumnRange outerCall, 0, 12 + assertColumnRange innerCall, 4, 10