From b60b9ed06795393d55aff740d6e7a7d31016f06c Mon Sep 17 00:00:00 2001 From: Alan Pierce Date: Mon, 17 Oct 2016 20:44:47 -0700 Subject: [PATCH] fix: properly set location for string tokens ending in a newline Fixes https://github.com/decaffeinate/decaffeinate/issues/457 The existing logic for computing the end location of a string was to take the end of the string contents, then add the delimiter length to last_column. For example, `"""abc"""` would have an end position three characters after the `c`. However, if a string ended in a newline, then the end location for the string contents would be one line above the end location for the string, so the proper fix is to move the end location to the next line, not just to shift it to the right. This avoids a bug where the location data would sometimes reference a non-existent location (one past the end of its line), which would confuse decaffeinate-parser. --- lib/coffee-script/lexer.js | 7 ++++++- src/lexer.coffee | 6 +++++- test/location.coffee | 23 +++++++++++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/lib/coffee-script/lexer.js b/lib/coffee-script/lexer.js index a9a3dd175d..0adce36d9c 100644 --- a/lib/coffee-script/lexer.js +++ b/lib/coffee-script/lexer.js @@ -644,7 +644,12 @@ } firstToken = tokens[0], lastToken = tokens[tokens.length - 1]; firstToken[2].first_column -= delimiter.length; - lastToken[2].last_column += delimiter.length; + if (lastToken[1].substr(-1) === '\n') { + lastToken[2].last_line += 1; + lastToken[2].last_column = delimiter.length - 1; + } else { + lastToken[2].last_column += delimiter.length; + } if (lastToken[1].length === 0) { lastToken[2].last_column -= 1; } diff --git a/src/lexer.coffee b/src/lexer.coffee index e38c22b1c6..79a8a6ad12 100644 --- a/src/lexer.coffee +++ b/src/lexer.coffee @@ -530,7 +530,11 @@ exports.Lexer = class Lexer [firstToken, ..., lastToken] = tokens firstToken[2].first_column -= delimiter.length - lastToken[2].last_column += delimiter.length + if lastToken[1].substr(-1) == '\n' + lastToken[2].last_line += 1 + lastToken[2].last_column = delimiter.length - 1 + else + lastToken[2].last_column += delimiter.length lastToken[2].last_column -= 1 if lastToken[1].length is 0 {tokens, index: offsetInChunk + delimiter.length} diff --git a/test/location.coffee b/test/location.coffee index a2f855a7ba..efec69e9c1 100644 --- a/test/location.coffee +++ b/test/location.coffee @@ -528,6 +528,29 @@ test "Verify real CALL_END tokens have the right position", -> eq callEnd[2].first_column, startIndex + 2 eq callEnd[2].last_column, startIndex + 2 +test "Verify normal heredocs have the right position", -> + source = ''' + """ + a""" + ''' + [stringToken] = CoffeeScript.tokens source + eq stringToken[2].first_line, 0 + eq stringToken[2].first_column, 0 + eq stringToken[2].last_line, 1 + eq stringToken[2].last_column, 3 + +test "Verify heredocs ending with a newline have the right position", -> + source = ''' + """ + a + """ + ''' + [stringToken] = CoffeeScript.tokens source + eq stringToken[2].first_line, 0 + eq stringToken[2].first_column, 0 + eq stringToken[2].last_line, 2 + eq stringToken[2].last_column, 2 + test "Verify all tokens get a location", -> doesNotThrow -> tokens = CoffeeScript.tokens testScript