From 5992896d76e0f11302684f4c6691ed19b8cde9f3 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Mon, 10 Dec 2018 16:03:45 +0300 Subject: [PATCH] Relax assertion in parser for a recovery case with annotations ^KT-24937 Fixed --- .../kotlin/parsing/KotlinParsing.java | 9 +- .../testData/psi/annotation/at/kt21055.kt | 9 + .../testData/psi/annotation/at/kt21055.txt | 240 ++++++++++++++++++ .../at/recoveryWhitespaceBeforeColon.kt | 6 + .../at/recoveryWhitespaceBeforeColon.txt | 64 +++++ .../at/recoveryWhitespaceBeforeColon_ERR.kt | 24 ++ .../at/recoveryWhitespaceBeforeColon_ERR.txt | 143 +++++++++++ .../kotlin/parsing/ParsingTestGenerated.java | 15 ++ 8 files changed, 509 insertions(+), 1 deletion(-) create mode 100644 compiler/testData/psi/annotation/at/kt21055.kt create mode 100644 compiler/testData/psi/annotation/at/kt21055.txt create mode 100644 compiler/testData/psi/annotation/at/recoveryWhitespaceBeforeColon.kt create mode 100644 compiler/testData/psi/annotation/at/recoveryWhitespaceBeforeColon.txt create mode 100644 compiler/testData/psi/annotation/at/recoveryWhitespaceBeforeColon_ERR.kt create mode 100644 compiler/testData/psi/annotation/at/recoveryWhitespaceBeforeColon_ERR.txt diff --git a/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinParsing.java b/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinParsing.java index 1c60cf79843..2b9628edd9f 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinParsing.java +++ b/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinParsing.java @@ -750,7 +750,10 @@ public class KotlinParsing extends AbstractKotlinParsing { */ private boolean parseAnnotation(AnnotationParsingMode mode) { assert _at(IDENTIFIER) || - (_at(AT) && !WHITE_SPACE_OR_COMMENT_BIT_SET.contains(myBuilder.rawLookup(1))); + // We have "@ann" or "@:ann" or "@ :ann", but not "@ ann" + // (it's guaranteed that call sites do not allow the latter case) + (_at(AT) && (!isNextRawTokenCommentOrWhitespace() || lookahead(1) == COLON)) + : "Invalid annotation prefix"; PsiBuilder.Marker annotation = mark(); @@ -780,6 +783,10 @@ public class KotlinParsing extends AbstractKotlinParsing { return true; } + private boolean isNextRawTokenCommentOrWhitespace() { + return WHITE_SPACE_OR_COMMENT_BIT_SET.contains(myBuilder.rawLookup(1)); + } + public enum NameParsingMode { REQUIRED, ALLOWED, diff --git a/compiler/testData/psi/annotation/at/kt21055.kt b/compiler/testData/psi/annotation/at/kt21055.kt new file mode 100644 index 00000000000..c2f899c4749 --- /dev/null +++ b/compiler/testData/psi/annotation/at/kt21055.kt @@ -0,0 +1,9 @@ +fun main(args: Array) { + val c = C(1, 2, 3, 4) + val number = when (c) { + match B(e1, e2 @ : Pair if(l > r), e3) @ a: A @ m -> e1 + l + r + match (_, p :Pair, _) -> 20 + else -> 40 + } + println(number) +} diff --git a/compiler/testData/psi/annotation/at/kt21055.txt b/compiler/testData/psi/annotation/at/kt21055.txt new file mode 100644 index 00000000000..e25ff567661 --- /dev/null +++ b/compiler/testData/psi/annotation/at/kt21055.txt @@ -0,0 +1,240 @@ +KtFile: kt21055.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 ') + PROPERTY + PsiElement(val)('val') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('c') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + CALL_EXPRESSION + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('C') + VALUE_ARGUMENT_LIST + PsiElement(LPAR)('(') + VALUE_ARGUMENT + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('1') + PsiElement(COMMA)(',') + PsiWhiteSpace(' ') + VALUE_ARGUMENT + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('2') + PsiElement(COMMA)(',') + PsiWhiteSpace(' ') + VALUE_ARGUMENT + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('3') + PsiElement(COMMA)(',') + PsiWhiteSpace(' ') + VALUE_ARGUMENT + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('4') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n ') + PROPERTY + PsiElement(val)('val') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('number') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + WHEN + PsiElement(when)('when') + PsiWhiteSpace(' ') + PsiElement(LPAR)('(') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('c') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + WHEN_ENTRY + WHEN_CONDITION_WITH_EXPRESSION + BINARY_EXPRESSION + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('match') + PsiWhiteSpace(' ') + OPERATION_REFERENCE + PsiElement(IDENTIFIER)('B') + PARENTHESIZED + PsiElement(LPAR)('(') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('e1') + PsiErrorElement:Expecting ')' + + PsiElement(COMMA)(',') + PsiWhiteSpace(' ') + WHEN_CONDITION_WITH_EXPRESSION + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('e2') + PsiWhiteSpace(' ') + PsiErrorElement:Expecting '->' + PsiElement(AT)('@') + PsiErrorElement:Expecting an expression + + PsiWhiteSpace(' ') + WHEN_ENTRY + WHEN_CONDITION_WITH_EXPRESSION + PsiErrorElement:Expecting an expression + + PsiErrorElement:Expecting '->' + PsiElement(COLON)(':') + PsiWhiteSpace(' ') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('Pair') + PsiWhiteSpace(' ') + WHEN_ENTRY + WHEN_CONDITION_WITH_EXPRESSION + IF + PsiElement(if)('if') + PsiElement(LPAR)('(') + CONDITION + BINARY_EXPRESSION + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('l') + PsiWhiteSpace(' ') + OPERATION_REFERENCE + PsiElement(GT)('>') + PsiWhiteSpace(' ') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('r') + PsiElement(RPAR)(')') + THEN + PsiErrorElement:Expecting an expression + + PsiElement(COMMA)(',') + PsiWhiteSpace(' ') + WHEN_CONDITION_WITH_EXPRESSION + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('e3') + PsiErrorElement:Expecting '->' + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + ANNOTATED_EXPRESSION + PsiErrorElement:Expected annotation identifier after '@' + PsiElement(AT)('@') + PsiWhiteSpace(' ') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('a') + WHEN_ENTRY + WHEN_CONDITION_WITH_EXPRESSION + PsiErrorElement:Expecting an expression + + PsiErrorElement:Expecting '->' + PsiElement(COLON)(':') + PsiWhiteSpace(' ') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('A') + PsiWhiteSpace(' ') + WHEN_ENTRY + WHEN_CONDITION_WITH_EXPRESSION + ANNOTATED_EXPRESSION + PsiErrorElement:Expected annotation identifier after '@' + PsiElement(AT)('@') + PsiWhiteSpace(' ') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('m') + PsiWhiteSpace(' ') + PsiElement(ARROW)('->') + PsiWhiteSpace(' ') + BINARY_EXPRESSION + BINARY_EXPRESSION + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('e1') + PsiWhiteSpace(' ') + OPERATION_REFERENCE + PsiElement(PLUS)('+') + PsiWhiteSpace(' ') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('l') + PsiWhiteSpace(' ') + OPERATION_REFERENCE + PsiElement(PLUS)('+') + PsiWhiteSpace(' ') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('r') + PsiWhiteSpace('\n ') + WHEN_ENTRY + WHEN_CONDITION_WITH_EXPRESSION + CALL_EXPRESSION + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('match') + PsiWhiteSpace(' ') + VALUE_ARGUMENT_LIST + PsiElement(LPAR)('(') + VALUE_ARGUMENT + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('_') + PsiElement(COMMA)(',') + PsiWhiteSpace(' ') + VALUE_ARGUMENT + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('p') + PsiWhiteSpace(' ') + PsiErrorElement:Unexpected type specification + PsiElement(COLON)(':') + PsiElement(IDENTIFIER)('Pair') + PsiElement(COMMA)(',') + PsiWhiteSpace(' ') + VALUE_ARGUMENT + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('_') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + PsiElement(ARROW)('->') + PsiWhiteSpace(' ') + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('20') + PsiWhiteSpace('\n ') + WHEN_ENTRY + PsiElement(else)('else') + PsiWhiteSpace(' ') + PsiElement(ARROW)('->') + PsiWhiteSpace(' ') + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('40') + PsiWhiteSpace('\n ') + PsiElement(RBRACE)('}') + PsiWhiteSpace('\n ') + CALL_EXPRESSION + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('println') + VALUE_ARGUMENT_LIST + PsiElement(LPAR)('(') + VALUE_ARGUMENT + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('number') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n') + PsiElement(RBRACE)('}') \ No newline at end of file diff --git a/compiler/testData/psi/annotation/at/recoveryWhitespaceBeforeColon.kt b/compiler/testData/psi/annotation/at/recoveryWhitespaceBeforeColon.kt new file mode 100644 index 00000000000..5feda454271 --- /dev/null +++ b/compiler/testData/psi/annotation/at/recoveryWhitespaceBeforeColon.kt @@ -0,0 +1,6 @@ +// These two cases were working before, added them just in case +@set: ann1 +var x1: Int = 1 + +@set: ann2 +var x2: Int = 2 diff --git a/compiler/testData/psi/annotation/at/recoveryWhitespaceBeforeColon.txt b/compiler/testData/psi/annotation/at/recoveryWhitespaceBeforeColon.txt new file mode 100644 index 00000000000..88171c016b9 --- /dev/null +++ b/compiler/testData/psi/annotation/at/recoveryWhitespaceBeforeColon.txt @@ -0,0 +1,64 @@ +KtFile: recoveryWhitespaceBeforeColon.kt + PACKAGE_DIRECTIVE + + IMPORT_LIST + + PROPERTY + PsiComment(EOL_COMMENT)('// These two cases were working before, added them just in case') + PsiWhiteSpace('\n') + MODIFIER_LIST + ANNOTATION_ENTRY + PsiElement(AT)('@') + ANNOTATION_TARGET + PsiElement(set)('set') + PsiElement(COLON)(':') + PsiWhiteSpace(' ') + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('ann1') + PsiWhiteSpace('\n') + PsiElement(var)('var') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('x1') + PsiElement(COLON)(':') + PsiWhiteSpace(' ') + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('Int') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('1') + PsiWhiteSpace('\n\n') + PROPERTY + MODIFIER_LIST + ANNOTATION_ENTRY + PsiElement(AT)('@') + ANNOTATION_TARGET + PsiElement(set)('set') + PsiElement(COLON)(':') + PsiWhiteSpace(' ') + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('ann2') + PsiWhiteSpace('\n') + PsiElement(var)('var') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('x2') + PsiElement(COLON)(':') + PsiWhiteSpace(' ') + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('Int') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('2') diff --git a/compiler/testData/psi/annotation/at/recoveryWhitespaceBeforeColon_ERR.kt b/compiler/testData/psi/annotation/at/recoveryWhitespaceBeforeColon_ERR.kt new file mode 100644 index 00000000000..dabe6866794 --- /dev/null +++ b/compiler/testData/psi/annotation/at/recoveryWhitespaceBeforeColon_ERR.kt @@ -0,0 +1,24 @@ +@ :ann1 +class A1 + +@ /* */ // +:ann2 +class A2 + +@: ann3 +class A3 + +@ : ann4 +class A4 + +@set :ann5 +class A5 + +@ set : ann8 +class A8 + +@sset : ann9 +class A9 + +@ : +class A10 diff --git a/compiler/testData/psi/annotation/at/recoveryWhitespaceBeforeColon_ERR.txt b/compiler/testData/psi/annotation/at/recoveryWhitespaceBeforeColon_ERR.txt new file mode 100644 index 00000000000..27e8f10a7bc --- /dev/null +++ b/compiler/testData/psi/annotation/at/recoveryWhitespaceBeforeColon_ERR.txt @@ -0,0 +1,143 @@ +KtFile: recoveryWhitespaceBeforeColon_ERR.kt + FILE_ANNOTATION_LIST + ANNOTATION_ENTRY + PsiElement(AT)('@') + PsiWhiteSpace(' ') + PsiErrorElement:Expected annotation target before ':' + PsiElement(COLON)(':') + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('ann1') + PACKAGE_DIRECTIVE + + IMPORT_LIST + + PsiWhiteSpace('\n') + CLASS + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('A1') + PsiWhiteSpace('\n\n') + CLASS + MODIFIER_LIST + ANNOTATION_ENTRY + PsiElement(AT)('@') + PsiWhiteSpace(' ') + PsiComment(BLOCK_COMMENT)('/* */') + PsiWhiteSpace(' ') + PsiComment(EOL_COMMENT)('//') + PsiWhiteSpace('\n') + PsiErrorElement:Expected annotation target before ':' + PsiElement(COLON)(':') + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('ann2') + PsiWhiteSpace('\n') + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('A2') + PsiWhiteSpace('\n\n') + CLASS + MODIFIER_LIST + ANNOTATION_ENTRY + PsiElement(AT)('@') + PsiErrorElement:Expected annotation target before ':' + PsiElement(COLON)(':') + PsiWhiteSpace(' ') + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('ann3') + PsiWhiteSpace('\n') + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('A3') + PsiWhiteSpace('\n\n') + CLASS + MODIFIER_LIST + ANNOTATION_ENTRY + PsiElement(AT)('@') + PsiWhiteSpace(' ') + PsiErrorElement:Expected annotation target before ':' + PsiElement(COLON)(':') + PsiWhiteSpace(' ') + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('ann4') + PsiWhiteSpace('\n') + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('A4') + PsiWhiteSpace('\n\n') + CLASS + MODIFIER_LIST + ANNOTATION_ENTRY + PsiElement(AT)('@') + ANNOTATION_TARGET + PsiElement(set)('set') + PsiWhiteSpace(' ') + PsiElement(COLON)(':') + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('ann5') + PsiWhiteSpace('\n') + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('A5') + PsiWhiteSpace('\n\n') + MODIFIER_LIST + PsiErrorElement:Expected annotation identifier after '@' + PsiElement(AT)('@') + PsiWhiteSpace(' ') + PsiErrorElement:Expecting a top level declaration + PsiElement(IDENTIFIER)('set') + PsiWhiteSpace(' ') + PsiErrorElement:Expecting a top level declaration + PsiElement(COLON)(':') + PsiWhiteSpace(' ') + PsiErrorElement:Expecting a top level declaration + PsiElement(IDENTIFIER)('ann8') + PsiWhiteSpace('\n') + CLASS + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('A8') + PsiWhiteSpace('\n\n') + CLASS + MODIFIER_LIST + ANNOTATION_ENTRY + PsiElement(AT)('@') + PsiErrorElement:Expected annotation target before ':' + PsiElement(IDENTIFIER)('sset') + PsiWhiteSpace(' ') + PsiElement(COLON)(':') + PsiWhiteSpace(' ') + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('ann9') + PsiWhiteSpace('\n') + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('A9') + PsiWhiteSpace('\n\n') + CLASS + MODIFIER_LIST + PsiErrorElement:Expected annotation identifier after ':' + PsiElement(AT)('@') + PsiWhiteSpace(' ') + PsiElement(COLON)(':') + PsiWhiteSpace('\n') + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('A10') diff --git a/compiler/tests/org/jetbrains/kotlin/parsing/ParsingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/parsing/ParsingTestGenerated.java index fdd9741607b..abab9a9ca6b 100644 --- a/compiler/tests/org/jetbrains/kotlin/parsing/ParsingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/parsing/ParsingTestGenerated.java @@ -860,6 +860,11 @@ public class ParsingTestGenerated extends AbstractParsingTest { runTest("compiler/testData/psi/annotation/at/expressionJustAtTyped.kt"); } + @TestMetadata("kt21055.kt") + public void testKt21055() throws Exception { + runTest("compiler/testData/psi/annotation/at/kt21055.kt"); + } + @TestMetadata("modifierAtFileStart.kt") public void testModifierAtFileStart() throws Exception { runTest("compiler/testData/psi/annotation/at/modifierAtFileStart.kt"); @@ -870,6 +875,16 @@ public class ParsingTestGenerated extends AbstractParsingTest { runTest("compiler/testData/psi/annotation/at/primaryConstructor.kt"); } + @TestMetadata("recoveryWhitespaceBeforeColon.kt") + public void testRecoveryWhitespaceBeforeColon() throws Exception { + runTest("compiler/testData/psi/annotation/at/recoveryWhitespaceBeforeColon.kt"); + } + + @TestMetadata("recoveryWhitespaceBeforeColon_ERR.kt") + public void testRecoveryWhitespaceBeforeColon_ERR() throws Exception { + runTest("compiler/testData/psi/annotation/at/recoveryWhitespaceBeforeColon_ERR.kt"); + } + @TestMetadata("validDeclarations.kt") public void testValidDeclarations() throws Exception { runTest("compiler/testData/psi/annotation/at/validDeclarations.kt");