From fc6c7306b7d2d0b3a84a263345c1a4e258dae988 Mon Sep 17 00:00:00 2001 From: Alan Pierce Date: Sun, 2 Oct 2016 19:45:53 -0700 Subject: [PATCH] Fix location data for implicit CALL_END tokens Fixes https://github.com/decaffeinate/decaffeinate/issues/446 In addition to OUTDENT tokens, CALL_END tokens can also be virtual tokens without a real location, and sometimes they end up with a location that's incorrect. --- lib/coffee-script/rewriter.js | 2 +- src/rewriter.coffee | 3 ++- test/location.coffee | 41 +++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/lib/coffee-script/rewriter.js b/lib/coffee-script/rewriter.js index 0049bf538a..e64adcc05d 100644 --- a/lib/coffee-script/rewriter.js +++ b/lib/coffee-script/rewriter.js @@ -376,7 +376,7 @@ Rewriter.prototype.fixOutdentLocationData = function() { return this.scanTokens(function(token, i, tokens) { var prevLocationData; - if (token[0] !== 'OUTDENT') { + if (!(token[0] === 'OUTDENT' || (token.generated && token[0] === 'CALL_END'))) { return 1; } prevLocationData = tokens[i - 1][2]; diff --git a/src/rewriter.coffee b/src/rewriter.coffee index 941b5e3682..3f1e3fc154 100644 --- a/src/rewriter.coffee +++ b/src/rewriter.coffee @@ -374,7 +374,8 @@ class exports.Rewriter # location corresponding to the last "real" token under the node. fixOutdentLocationData: -> @scanTokens (token, i, tokens) -> - return 1 unless token[0] is 'OUTDENT' + return 1 unless token[0] is 'OUTDENT' or + (token.generated and token[0] is 'CALL_END') prevLocationData = tokens[i - 1][2] token[2] = first_line: prevLocationData.last_line diff --git a/test/location.coffee b/test/location.coffee index 1782946d82..a2f855a7ba 100644 --- a/test/location.coffee +++ b/test/location.coffee @@ -487,6 +487,47 @@ test "Verify OUTDENT tokens are located at the end of the previous token", -> eq outdent[2].last_line, number[2].last_line eq outdent[2].last_column, number[2].last_column +test "Verify OUTDENT and CALL_END tokens are located at the end of the previous token", -> + source = ''' + a = b { + c: -> + d e, + if f + g {}, + if h + i {} + } + ''' + tokens = CoffeeScript.tokens source + [..., closeCurly1, callEnd1, outdent1, outdent2, callEnd2, outdent3, outdent4, + callEnd3, outdent5, outdent6, closeCurly2, callEnd4, terminator] = tokens + eq closeCurly1[0], '}' + assertAtCloseCurly = (token) -> + eq token[2].first_line, closeCurly1[2].last_line + eq token[2].first_column, closeCurly1[2].last_column + eq token[2].last_line, closeCurly1[2].last_line + eq token[2].last_column, closeCurly1[2].last_column + + for token in [outdent1, outdent2, outdent3, outdent4, outdent5, outdent6] + eq token[0], 'OUTDENT' + assertAtCloseCurly(token) + for token in [callEnd1, callEnd2, callEnd3] + eq token[0], 'CALL_END' + assertAtCloseCurly(token) + +test "Verify real CALL_END tokens have the right position", -> + source = ''' + a() + ''' + tokens = CoffeeScript.tokens source + [identifier, callStart, callEnd, terminator] = tokens + startIndex = identifier[2].first_column + eq identifier[2].last_column, startIndex + eq callStart[2].first_column, startIndex + 1 + eq callStart[2].last_column, startIndex + 1 + eq callEnd[2].first_column, startIndex + 2 + eq callEnd[2].last_column, startIndex + 2 + test "Verify all tokens get a location", -> doesNotThrow -> tokens = CoffeeScript.tokens testScript