From afc0892d1f4ec8e97a67115a913dff473faebf1a Mon Sep 17 00:00:00 2001 From: Nikolay Krasko Date: Wed, 15 Mar 2017 16:52:50 +0300 Subject: [PATCH] Synchronize state of parser and lexer for LONG_TEMPLATE_ENTRY (KT-14865) Lexer monitors "long string template" state end and will produce LONG_TEMPLATE_ENTRY_END token when it is reached. If parser continues without waiting for it, it will eventually get handling token that will produce irrelevant error. Such behaviour also breaks lazy elements (LAMBDA_EXPRESSION in this case) contract: range of parsed text in eager mode should be same to one parsed in lazy mode. #KT-14865 Fixed --- .../parsing/KotlinExpressionParsing.java | 19 +++++- .../recovery/lambdaExpressionInString_1.kt | 6 ++ .../recovery/lambdaExpressionInString_1.txt | 60 +++++++++++++++++ .../recovery/lambdaExpressionInString_2.kt | 4 ++ .../recovery/lambdaExpressionInString_2.txt | 66 +++++++++++++++++++ .../kotlin/parsing/ParsingTestGenerated.java | 12 ++++ 6 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 compiler/testData/psi/recovery/lambdaExpressionInString_1.kt create mode 100644 compiler/testData/psi/recovery/lambdaExpressionInString_1.txt create mode 100644 compiler/testData/psi/recovery/lambdaExpressionInString_2.kt create mode 100644 compiler/testData/psi/recovery/lambdaExpressionInString_2.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinExpressionParsing.java b/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinExpressionParsing.java index cd6102462b8..caffeedc21c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinExpressionParsing.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinExpressionParsing.java @@ -764,9 +764,24 @@ public class KotlinExpressionParsing extends AbstractKotlinParsing { advance(); // LONG_TEMPLATE_ENTRY_START - parseExpression(); + while (!eof()) { + int offset = myBuilder.getCurrentOffset(); + + parseExpression(); + + if (_at(LONG_TEMPLATE_ENTRY_END)) { + advance(); + break; + } + else { + error("Expecting '}'"); + if (offset == myBuilder.getCurrentOffset()) { + // Prevent hang if can't advance with parseExpression() + advance(); + } + } + } - expect(LONG_TEMPLATE_ENTRY_END, "Expecting '}'", TokenSet.create(CLOSING_QUOTE, DANGLING_NEWLINE, REGULAR_STRING_PART, ESCAPE_SEQUENCE, SHORT_TEMPLATE_ENTRY_START)); longTemplateEntry.done(LONG_STRING_TEMPLATE_ENTRY); } else { diff --git a/compiler/testData/psi/recovery/lambdaExpressionInString_1.kt b/compiler/testData/psi/recovery/lambdaExpressionInString_1.kt new file mode 100644 index 00000000000..7b96f7230fe --- /dev/null +++ b/compiler/testData/psi/recovery/lambdaExpressionInString_1.kt @@ -0,0 +1,6 @@ +fun some1() { + { + """${dM$ { "$a -> $" }} + + """} +} \ No newline at end of file diff --git a/compiler/testData/psi/recovery/lambdaExpressionInString_1.txt b/compiler/testData/psi/recovery/lambdaExpressionInString_1.txt new file mode 100644 index 00000000000..05306c3a48f --- /dev/null +++ b/compiler/testData/psi/recovery/lambdaExpressionInString_1.txt @@ -0,0 +1,60 @@ +JetFile: lambdaExpressionInString_1.kt + PACKAGE_DIRECTIVE + + IMPORT_LIST + + FUN + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('some1') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + BLOCK + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + LAMBDA_EXPRESSION + FUNCTION_LITERAL + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + BLOCK + STRING_TEMPLATE + PsiElement(OPEN_QUOTE)('"""') + LONG_STRING_TEMPLATE_ENTRY + PsiElement(LONG_TEMPLATE_ENTRY_START)('${') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('dM') + PsiErrorElement:Expecting '}' + + PsiElement(BAD_CHARACTER)('$') + PsiWhiteSpace(' ') + LAMBDA_EXPRESSION + FUNCTION_LITERAL + PsiElement(LBRACE)('{') + PsiWhiteSpace(' ') + BLOCK + STRING_TEMPLATE + PsiElement(OPEN_QUOTE)('"') + SHORT_STRING_TEMPLATE_ENTRY + PsiElement(SHORT_TEMPLATE_ENTRY_START)('$') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('a') + LITERAL_STRING_TEMPLATE_ENTRY + PsiElement(REGULAR_STRING_PART)(' -> ') + LITERAL_STRING_TEMPLATE_ENTRY + PsiElement(REGULAR_STRING_PART)('$') + PsiElement(CLOSING_QUOTE)('"') + PsiWhiteSpace(' ') + PsiElement(RBRACE)('}') + PsiElement(LONG_TEMPLATE_ENTRY_END)('}') + LITERAL_STRING_TEMPLATE_ENTRY + PsiElement(REGULAR_STRING_PART)('\n') + LITERAL_STRING_TEMPLATE_ENTRY + PsiElement(REGULAR_STRING_PART)('\n') + LITERAL_STRING_TEMPLATE_ENTRY + PsiElement(REGULAR_STRING_PART)(' ') + PsiElement(CLOSING_QUOTE)('"""') + PsiElement(RBRACE)('}') + PsiWhiteSpace('\n') + PsiElement(RBRACE)('}') \ No newline at end of file diff --git a/compiler/testData/psi/recovery/lambdaExpressionInString_2.kt b/compiler/testData/psi/recovery/lambdaExpressionInString_2.kt new file mode 100644 index 00000000000..aff8ecc05c0 --- /dev/null +++ b/compiler/testData/psi/recovery/lambdaExpressionInString_2.kt @@ -0,0 +1,4 @@ +fun main(args: Array) { + { "${"} + { "" } +} \ No newline at end of file diff --git a/compiler/testData/psi/recovery/lambdaExpressionInString_2.txt b/compiler/testData/psi/recovery/lambdaExpressionInString_2.txt new file mode 100644 index 00000000000..36a5be6974e --- /dev/null +++ b/compiler/testData/psi/recovery/lambdaExpressionInString_2.txt @@ -0,0 +1,66 @@ +JetFile: lambdaExpressionInString_2.kt + PACKAGE_DIRECTIVE + + IMPORT_LIST + + FUN + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('main') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + VALUE_PARAMETER + PsiElement(IDENTIFIER)('args') + PsiElement(COLON)(':') + PsiWhiteSpace(' ') + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('Array') + TYPE_ARGUMENT_LIST + PsiElement(LT)('<') + TYPE_PROJECTION + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('String') + PsiElement(GT)('>') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + BLOCK + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + LAMBDA_EXPRESSION + FUNCTION_LITERAL + PsiElement(LBRACE)('{') + PsiWhiteSpace(' ') + BLOCK + STRING_TEMPLATE + PsiElement(OPEN_QUOTE)('"') + LONG_STRING_TEMPLATE_ENTRY + PsiElement(LONG_TEMPLATE_ENTRY_START)('${') + STRING_TEMPLATE + PsiElement(OPEN_QUOTE)('"') + LITERAL_STRING_TEMPLATE_ENTRY + PsiElement(REGULAR_STRING_PART)('}') + PsiErrorElement:Expecting '"' + + PsiErrorElement:Expecting '}' + + PsiWhiteSpace('\n ') + LAMBDA_EXPRESSION + FUNCTION_LITERAL + PsiElement(LBRACE)('{') + PsiWhiteSpace(' ') + BLOCK + STRING_TEMPLATE + PsiElement(OPEN_QUOTE)('"') + PsiElement(CLOSING_QUOTE)('"') + PsiWhiteSpace(' ') + PsiElement(RBRACE)('}') + PsiWhiteSpace('\n') + PsiElement(LONG_TEMPLATE_ENTRY_END)('}') + PsiErrorElement:Expecting '"' + + PsiErrorElement:Expecting '}' + \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/parsing/ParsingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/parsing/ParsingTestGenerated.java index 5267ee6eef0..e02553f97d4 100644 --- a/compiler/tests/org/jetbrains/kotlin/parsing/ParsingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/parsing/ParsingTestGenerated.java @@ -2305,6 +2305,18 @@ public class ParsingTestGenerated extends AbstractParsingTest { doParsingTest(fileName); } + @TestMetadata("lambdaExpressionInString_1.kt") + public void testLambdaExpressionInString_1() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/psi/recovery/lambdaExpressionInString_1.kt"); + doParsingTest(fileName); + } + + @TestMetadata("lambdaExpressionInString_2.kt") + public void testLambdaExpressionInString_2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/psi/recovery/lambdaExpressionInString_2.kt"); + doParsingTest(fileName); + } + @TestMetadata("MissingCommaInConstructorValueParameterList.kt") public void testMissingCommaInConstructorValueParameterList() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/psi/recovery/MissingCommaInConstructorValueParameterList.kt");