From e4f54b5d2ec79f516c8560849a669d93fb1f57a7 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Fri, 15 May 2015 12:25:55 +0300 Subject: [PATCH] Parsing: support '@[ann1 ann2]', '@file:ann' and '@file:[ann]' cases --- .../jetbrains/kotlin/parsing/JetParsing.java | 91 ++++++++-- .../testData/psi/annotation/list/basic.kt | 11 ++ .../testData/psi/annotation/list/basic.txt | 156 ++++++++++++++++++ .../testData/psi/annotation/list/recovery.kt | 15 ++ .../testData/psi/annotation/list/recovery.txt | 135 +++++++++++++++ .../onFile/fileAnnotationInWrongPlace.kt | 16 ++ .../onFile/fileAnnotationInWrongPlace.txt | 104 ++++++++++++ .../annotation/onFile/manyAnnotationBlocks.kt | 3 + .../onFile/manyAnnotationBlocks.txt | 37 +++++ .../onFile/manyInOneAnnotationBlock.kt | 2 + .../onFile/manyInOneAnnotationBlock.txt | 35 ++++ .../onFile/nonFIleAnnotationBeforePackage.kt | 7 + .../onFile/nonFIleAnnotationBeforePackage.txt | 60 +++++++ .../testData/psi/annotation/onFile/single.kt | 2 + .../testData/psi/annotation/onFile/single.txt | 17 ++ ...houtFileAnnotationAndPackageDeclaration.kt | 3 + ...outFileAnnotationAndPackageDeclaration.txt | 40 +++++ .../psi/annotation/onFile/withoutPackage.kt | 4 + .../psi/annotation/onFile/withoutPackage.txt | 56 +++++++ .../withoutPackageWithSimpleAnnotation.kt | 4 + .../withoutPackageWithSimpleAnnotation.txt | 52 ++++++ .../nonFIleAnnotationBeforePackage.txt | 10 +- .../parsing/JetParsingTestGenerated.java | 78 +++++++++ grammar/src/attributes.grm | 6 +- grammar/src/toplevel.grm | 2 +- 25 files changed, 918 insertions(+), 28 deletions(-) create mode 100644 compiler/testData/psi/annotation/list/basic.kt create mode 100644 compiler/testData/psi/annotation/list/basic.txt create mode 100644 compiler/testData/psi/annotation/list/recovery.kt create mode 100644 compiler/testData/psi/annotation/list/recovery.txt create mode 100644 compiler/testData/psi/annotation/onFile/fileAnnotationInWrongPlace.kt create mode 100644 compiler/testData/psi/annotation/onFile/fileAnnotationInWrongPlace.txt create mode 100644 compiler/testData/psi/annotation/onFile/manyAnnotationBlocks.kt create mode 100644 compiler/testData/psi/annotation/onFile/manyAnnotationBlocks.txt create mode 100644 compiler/testData/psi/annotation/onFile/manyInOneAnnotationBlock.kt create mode 100644 compiler/testData/psi/annotation/onFile/manyInOneAnnotationBlock.txt create mode 100644 compiler/testData/psi/annotation/onFile/nonFIleAnnotationBeforePackage.kt create mode 100644 compiler/testData/psi/annotation/onFile/nonFIleAnnotationBeforePackage.txt create mode 100644 compiler/testData/psi/annotation/onFile/single.kt create mode 100644 compiler/testData/psi/annotation/onFile/single.txt create mode 100644 compiler/testData/psi/annotation/onFile/withoutFileAnnotationAndPackageDeclaration.kt create mode 100644 compiler/testData/psi/annotation/onFile/withoutFileAnnotationAndPackageDeclaration.txt create mode 100644 compiler/testData/psi/annotation/onFile/withoutPackage.kt create mode 100644 compiler/testData/psi/annotation/onFile/withoutPackage.txt create mode 100644 compiler/testData/psi/annotation/onFile/withoutPackageWithSimpleAnnotation.kt create mode 100644 compiler/testData/psi/annotation/onFile/withoutPackageWithSimpleAnnotation.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/parsing/JetParsing.java b/compiler/frontend/src/org/jetbrains/kotlin/parsing/JetParsing.java index 46023985dcd..9792c947e44 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/parsing/JetParsing.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/parsing/JetParsing.java @@ -531,18 +531,44 @@ public class JetParsing extends AbstractJetParsing { */ private boolean parseAnnotation(AnnotationParsingMode mode) { if (at(LBRACKET)) { - return parseAnnotationList(mode); + return parseAnnotationList(mode, false); } else if (mode.allowShortAnnotations && at(IDENTIFIER)) { - parseAnnotationEntry(); - return true; + return parseAnnotationEntry(mode); } else if (at(AT)) { - if (myBuilder.rawLookup(1) == IDENTIFIER) { - parseAnnotationEntry(); + IElementType nextRawToken = myBuilder.rawLookup(1); + IElementType tokenToMatch = nextRawToken; + boolean isTargetedAnnotation = false; + + if ((nextRawToken == IDENTIFIER || nextRawToken == FILE_KEYWORD) && lookahead(2) == COLON) { + tokenToMatch = lookahead(3); + isTargetedAnnotation = true; + } + else if (lookahead(1) == COLON) { + // recovery for "@:ann" + isTargetedAnnotation = true; + tokenToMatch = lookahead(2); + } + + if (tokenToMatch == IDENTIFIER) { + return parseAnnotationEntry(mode); + } + else if (tokenToMatch == LBRACKET) { + return parseAnnotationList(mode, true); } else { - errorAndAdvance("Expected annotation identifier after '@'", 1); // AT + if (isTargetedAnnotation) { + if (lookahead(1) == COLON) { + errorAndAdvance("Expected annotation identifier after ':'", 2); // AT, COLON + } + else { + errorAndAdvance("Expected annotation identifier after '@file:'", 3); // AT, FILE_KEYWORD, COLON + } + } + else { + errorAndAdvance("Expected annotation identifier after '@'", 1); // AT + } } return true; } @@ -550,11 +576,14 @@ public class JetParsing extends AbstractJetParsing { return false; } - private boolean parseAnnotationList(AnnotationParsingMode mode) { + private boolean parseAnnotationList(AnnotationParsingMode mode, boolean expectAtSymbol) { + assert !expectAtSymbol || _at(AT); + assert expectAtSymbol || _at(LBRACKET); PsiBuilder.Marker annotation = mark(); myBuilder.disableNewlines(); - advance(); // LBRACKET + + advance(); // AT or LBRACKET if (!parseAnnotationTargetIfNeeded(mode)) { annotation.rollbackTo(); @@ -562,17 +591,22 @@ public class JetParsing extends AbstractJetParsing { return false; } - if (!at(IDENTIFIER)) { + if (expectAtSymbol) { + assert _at(LBRACKET); + advance(); // LBRACKET + } + + if (!at(IDENTIFIER) && !at(AT)) { error("Expecting a list of annotations"); } else { - parseAnnotationEntry(); - while (at(COMMA)) { - errorAndAdvance("No commas needed to separate annotations"); - } + while (at(IDENTIFIER) || at(AT)) { + if (at(AT)) { + errorAndAdvance("No '@' needed in annotation list"); // AT + continue; + } - while (at(IDENTIFIER)) { - parseAnnotationEntry(); + parseAnnotationEntry(ALLOW_UNESCAPED_REGULAR_ANNOTATIONS); while (at(COMMA)) { errorAndAdvance("No commas needed to separate annotations"); } @@ -589,13 +623,26 @@ public class JetParsing extends AbstractJetParsing { // Returns true if we should continue parse annotation private boolean parseAnnotationTargetIfNeeded(AnnotationParsingMode mode) { if (mode.isFileAnnotationParsingMode) { + if (at(COLON)) { + // recovery for "@:ann" + errorAndAdvance("Expected 'file' keyword before ':'"); // COLON + return true; + } + + if (lookahead(1) == COLON && !at(FILE_KEYWORD) && at(IDENTIFIER)) { + // recovery for "@fil:ann" + errorAndAdvance("Expected 'file' keyword as target"); // IDENTIFIER + advance(); // COLON + return true; + } + if (mode == FILE_ANNOTATIONS_WHEN_PACKAGE_OMITTED && !(at(FILE_KEYWORD) && lookahead(1) == COLON)) { return false; } String message = "Expecting \"" + FILE_KEYWORD.getValue() + COLON.getValue() + "\" prefix for file annotations"; expect(FILE_KEYWORD, message); - expect(COLON, message, TokenSet.create(IDENTIFIER, RBRACKET)); + expect(COLON, message, TokenSet.create(IDENTIFIER, RBRACKET, LBRACKET)); } else if (at(FILE_KEYWORD) && lookahead(1) == COLON) { errorAndAdvance("File annotations are only allowed before package declaration", 2); @@ -609,8 +656,9 @@ public class JetParsing extends AbstractJetParsing { * : SimpleName{"."} typeArguments? valueArguments? * ; */ - private void parseAnnotationEntry() { - assert _at(IDENTIFIER) || (_at(AT) && myBuilder.rawLookup(1) == IDENTIFIER); + private boolean parseAnnotationEntry(AnnotationParsingMode mode) { + assert _at(IDENTIFIER) || + (_at(AT) && !WHITE_SPACE_OR_COMMENT_BIT_SET.contains(myBuilder.rawLookup(1))); PsiBuilder.Marker annotation = mark(); @@ -618,6 +666,11 @@ public class JetParsing extends AbstractJetParsing { advance(); // AT } + if (!parseAnnotationTargetIfNeeded(mode)) { + annotation.rollbackTo(); + return false; + } + PsiBuilder.Marker reference = mark(); PsiBuilder.Marker typeReference = mark(); parseUserType(); @@ -630,6 +683,8 @@ public class JetParsing extends AbstractJetParsing { myExpressionParsing.parseValueArgumentList(); } annotation.done(ANNOTATION_ENTRY); + + return true; } public enum NameParsingMode { diff --git a/compiler/testData/psi/annotation/list/basic.kt b/compiler/testData/psi/annotation/list/basic.kt new file mode 100644 index 00000000000..fd866915f91 --- /dev/null +++ b/compiler/testData/psi/annotation/list/basic.kt @@ -0,0 +1,11 @@ +@[abc("") cde] +class A { + @[ abc + cde] + @[private] + fun foo() { + @[data inline] class Local {} + + @[suppress("a")] (1 + @[abc] 3) + } +} diff --git a/compiler/testData/psi/annotation/list/basic.txt b/compiler/testData/psi/annotation/list/basic.txt new file mode 100644 index 00000000000..137ca2dbd40 --- /dev/null +++ b/compiler/testData/psi/annotation/list/basic.txt @@ -0,0 +1,156 @@ +JetFile: basic.kt + PACKAGE_DIRECTIVE + + CLASS + MODIFIER_LIST + ANNOTATION + PsiElement(AT)('@') + PsiElement(LBRACKET)('[') + ANNOTATION_ENTRY + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('abc') + VALUE_ARGUMENT_LIST + PsiElement(LPAR)('(') + VALUE_ARGUMENT + STRING_TEMPLATE + PsiElement(OPEN_QUOTE)('"') + PsiElement(CLOSING_QUOTE)('"') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + ANNOTATION_ENTRY + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('cde') + PsiElement(RBRACKET)(']') + PsiWhiteSpace('\n') + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('A') + PsiWhiteSpace(' ') + CLASS_BODY + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + FUN + MODIFIER_LIST + ANNOTATION + PsiElement(AT)('@') + PsiElement(LBRACKET)('[') + PsiWhiteSpace(' ') + ANNOTATION_ENTRY + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('abc') + PsiWhiteSpace('\n ') + ANNOTATION_ENTRY + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('cde') + PsiElement(RBRACKET)(']') + PsiWhiteSpace('\n ') + ANNOTATION + PsiElement(AT)('@') + PsiElement(LBRACKET)('[') + ANNOTATION_ENTRY + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('private') + PsiElement(RBRACKET)(']') + PsiWhiteSpace('\n ') + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('foo') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + BLOCK + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + CLASS + MODIFIER_LIST + ANNOTATION + PsiElement(AT)('@') + PsiElement(LBRACKET)('[') + ANNOTATION_ENTRY + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('data') + PsiWhiteSpace(' ') + ANNOTATION_ENTRY + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('inline') + PsiElement(RBRACKET)(']') + PsiWhiteSpace(' ') + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('Local') + PsiWhiteSpace(' ') + CLASS_BODY + PsiElement(LBRACE)('{') + PsiElement(RBRACE)('}') + PsiWhiteSpace('\n\n ') + ANNOTATED_EXPRESSION + ANNOTATION + PsiElement(AT)('@') + PsiElement(LBRACKET)('[') + ANNOTATION_ENTRY + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('suppress') + VALUE_ARGUMENT_LIST + PsiElement(LPAR)('(') + VALUE_ARGUMENT + STRING_TEMPLATE + PsiElement(OPEN_QUOTE)('"') + LITERAL_STRING_TEMPLATE_ENTRY + PsiElement(REGULAR_STRING_PART)('a') + PsiElement(CLOSING_QUOTE)('"') + PsiElement(RPAR)(')') + PsiElement(RBRACKET)(']') + PsiWhiteSpace(' ') + PARENTHESIZED + PsiElement(LPAR)('(') + BINARY_EXPRESSION + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('1') + PsiWhiteSpace(' ') + OPERATION_REFERENCE + PsiElement(PLUS)('+') + PsiWhiteSpace(' ') + ANNOTATED_EXPRESSION + ANNOTATION + PsiElement(AT)('@') + PsiElement(LBRACKET)('[') + ANNOTATION_ENTRY + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('abc') + PsiElement(RBRACKET)(']') + PsiWhiteSpace(' ') + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('3') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n ') + PsiElement(RBRACE)('}') + PsiWhiteSpace('\n') + PsiElement(RBRACE)('}') \ No newline at end of file diff --git a/compiler/testData/psi/annotation/list/recovery.kt b/compiler/testData/psi/annotation/list/recovery.kt new file mode 100644 index 00000000000..c35c0cef4fc --- /dev/null +++ b/compiler/testData/psi/annotation/list/recovery.kt @@ -0,0 +1,15 @@ +@[] +class A { + @[] val x = 1 + + @[@q] + fun foo() { + @[] class A + } + + @[@q1 @ @q2] + fun foo2() {} + + @[ + fun bar() {} +} diff --git a/compiler/testData/psi/annotation/list/recovery.txt b/compiler/testData/psi/annotation/list/recovery.txt new file mode 100644 index 00000000000..b5da51e3e36 --- /dev/null +++ b/compiler/testData/psi/annotation/list/recovery.txt @@ -0,0 +1,135 @@ +JetFile: recovery.kt + PACKAGE_DIRECTIVE + + CLASS + MODIFIER_LIST + ANNOTATION + PsiElement(AT)('@') + PsiElement(LBRACKET)('[') + PsiErrorElement:Expecting a list of annotations + + PsiElement(RBRACKET)(']') + PsiWhiteSpace('\n') + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('A') + PsiWhiteSpace(' ') + CLASS_BODY + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + PROPERTY + MODIFIER_LIST + ANNOTATION + PsiElement(AT)('@') + PsiElement(LBRACKET)('[') + PsiErrorElement:Expecting a list of annotations + + PsiElement(RBRACKET)(']') + PsiWhiteSpace(' ') + PsiElement(val)('val') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('x') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('1') + PsiWhiteSpace('\n\n ') + FUN + MODIFIER_LIST + ANNOTATION + PsiElement(AT)('@') + PsiElement(LBRACKET)('[') + PsiErrorElement:No '@' needed in annotation list + PsiElement(AT)('@') + ANNOTATION_ENTRY + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('q') + PsiElement(RBRACKET)(']') + PsiWhiteSpace('\n ') + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('foo') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + BLOCK + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + CLASS + MODIFIER_LIST + ANNOTATION + PsiElement(AT)('@') + PsiElement(LBRACKET)('[') + PsiErrorElement:Expecting a list of annotations + + PsiElement(RBRACKET)(']') + PsiWhiteSpace(' ') + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('A') + PsiWhiteSpace('\n ') + PsiElement(RBRACE)('}') + PsiWhiteSpace('\n\n ') + FUN + MODIFIER_LIST + ANNOTATION + PsiElement(AT)('@') + PsiElement(LBRACKET)('[') + PsiErrorElement:No '@' needed in annotation list + PsiElement(AT)('@') + ANNOTATION_ENTRY + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('q1') + PsiWhiteSpace(' ') + PsiErrorElement:No '@' needed in annotation list + PsiElement(AT)('@') + PsiWhiteSpace(' ') + PsiErrorElement:No '@' needed in annotation list + PsiElement(AT)('@') + ANNOTATION_ENTRY + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('q2') + PsiElement(RBRACKET)(']') + PsiWhiteSpace('\n ') + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('foo2') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + BLOCK + PsiElement(LBRACE)('{') + PsiElement(RBRACE)('}') + PsiWhiteSpace('\n\n ') + FUN + MODIFIER_LIST + ANNOTATION + PsiElement(AT)('@') + PsiElement(LBRACKET)('[') + PsiErrorElement:Expecting a list of annotations + + PsiWhiteSpace('\n ') + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('bar') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + BLOCK + PsiElement(LBRACE)('{') + PsiElement(RBRACE)('}') + PsiWhiteSpace('\n') + PsiElement(RBRACE)('}') \ No newline at end of file diff --git a/compiler/testData/psi/annotation/onFile/fileAnnotationInWrongPlace.kt b/compiler/testData/psi/annotation/onFile/fileAnnotationInWrongPlace.kt new file mode 100644 index 00000000000..049c63634c8 --- /dev/null +++ b/compiler/testData/psi/annotation/onFile/fileAnnotationInWrongPlace.kt @@ -0,0 +1,16 @@ +package bar + +@file:foo +val prop + +@file:[bar baz] +fun func() {} + +@file:[baz] +class C + +@file: +trait T + +@file:[] +trait T \ No newline at end of file diff --git a/compiler/testData/psi/annotation/onFile/fileAnnotationInWrongPlace.txt b/compiler/testData/psi/annotation/onFile/fileAnnotationInWrongPlace.txt new file mode 100644 index 00000000000..278d222eac8 --- /dev/null +++ b/compiler/testData/psi/annotation/onFile/fileAnnotationInWrongPlace.txt @@ -0,0 +1,104 @@ +JetFile: fileAnnotationInWrongPlace.kt + PACKAGE_DIRECTIVE + PsiElement(package)('package') + PsiWhiteSpace(' ') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('bar') + PsiWhiteSpace('\n\n') + PROPERTY + MODIFIER_LIST + ANNOTATION_ENTRY + PsiElement(AT)('@') + PsiErrorElement:File annotations are only allowed before package declaration + PsiElement(file)('file') + PsiElement(COLON)(':') + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('foo') + PsiWhiteSpace('\n') + PsiElement(val)('val') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('prop') + PsiWhiteSpace('\n\n') + FUN + MODIFIER_LIST + ANNOTATION + PsiElement(AT)('@') + PsiErrorElement:File annotations are only allowed before package declaration + PsiElement(file)('file') + PsiElement(COLON)(':') + PsiElement(LBRACKET)('[') + ANNOTATION_ENTRY + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('bar') + PsiWhiteSpace(' ') + ANNOTATION_ENTRY + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('baz') + PsiElement(RBRACKET)(']') + PsiWhiteSpace('\n') + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('func') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + BLOCK + PsiElement(LBRACE)('{') + PsiElement(RBRACE)('}') + PsiWhiteSpace('\n\n') + CLASS + MODIFIER_LIST + ANNOTATION + PsiElement(AT)('@') + PsiErrorElement:File annotations are only allowed before package declaration + PsiElement(file)('file') + PsiElement(COLON)(':') + PsiElement(LBRACKET)('[') + ANNOTATION_ENTRY + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('baz') + PsiElement(RBRACKET)(']') + PsiWhiteSpace('\n') + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('C') + PsiWhiteSpace('\n\n') + CLASS + MODIFIER_LIST + PsiErrorElement:Expected annotation identifier after '@file:' + PsiElement(AT)('@') + PsiElement(IDENTIFIER)('file') + PsiElement(COLON)(':') + PsiWhiteSpace('\n') + PsiElement(trait)('trait') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('T') + PsiWhiteSpace('\n\n') + CLASS + MODIFIER_LIST + ANNOTATION + PsiElement(AT)('@') + PsiErrorElement:File annotations are only allowed before package declaration + PsiElement(file)('file') + PsiElement(COLON)(':') + PsiElement(LBRACKET)('[') + PsiErrorElement:Expecting a list of annotations + + PsiElement(RBRACKET)(']') + PsiWhiteSpace('\n') + PsiElement(trait)('trait') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('T') \ No newline at end of file diff --git a/compiler/testData/psi/annotation/onFile/manyAnnotationBlocks.kt b/compiler/testData/psi/annotation/onFile/manyAnnotationBlocks.kt new file mode 100644 index 00000000000..fff50bd9296 --- /dev/null +++ b/compiler/testData/psi/annotation/onFile/manyAnnotationBlocks.kt @@ -0,0 +1,3 @@ +@file:foo +@file:[bar baz] +package bar diff --git a/compiler/testData/psi/annotation/onFile/manyAnnotationBlocks.txt b/compiler/testData/psi/annotation/onFile/manyAnnotationBlocks.txt new file mode 100644 index 00000000000..48f6a80a1f5 --- /dev/null +++ b/compiler/testData/psi/annotation/onFile/manyAnnotationBlocks.txt @@ -0,0 +1,37 @@ +JetFile: manyAnnotationBlocks.kt + FILE_ANNOTATION_LIST + ANNOTATION_ENTRY + PsiElement(AT)('@') + PsiElement(file)('file') + PsiElement(COLON)(':') + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('foo') + PsiWhiteSpace('\n') + ANNOTATION + PsiElement(AT)('@') + PsiElement(file)('file') + PsiElement(COLON)(':') + PsiElement(LBRACKET)('[') + ANNOTATION_ENTRY + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('bar') + PsiWhiteSpace(' ') + ANNOTATION_ENTRY + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('baz') + PsiElement(RBRACKET)(']') + PsiWhiteSpace('\n') + PACKAGE_DIRECTIVE + PsiElement(package)('package') + PsiWhiteSpace(' ') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('bar') \ No newline at end of file diff --git a/compiler/testData/psi/annotation/onFile/manyInOneAnnotationBlock.kt b/compiler/testData/psi/annotation/onFile/manyInOneAnnotationBlock.kt new file mode 100644 index 00000000000..e890bc66d0a --- /dev/null +++ b/compiler/testData/psi/annotation/onFile/manyInOneAnnotationBlock.kt @@ -0,0 +1,2 @@ +@file: [foo bar baz] +package bar diff --git a/compiler/testData/psi/annotation/onFile/manyInOneAnnotationBlock.txt b/compiler/testData/psi/annotation/onFile/manyInOneAnnotationBlock.txt new file mode 100644 index 00000000000..3f5ec3522f7 --- /dev/null +++ b/compiler/testData/psi/annotation/onFile/manyInOneAnnotationBlock.txt @@ -0,0 +1,35 @@ +JetFile: manyInOneAnnotationBlock.kt + FILE_ANNOTATION_LIST + ANNOTATION + PsiElement(AT)('@') + PsiElement(file)('file') + PsiElement(COLON)(':') + PsiWhiteSpace(' ') + PsiElement(LBRACKET)('[') + ANNOTATION_ENTRY + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('foo') + PsiWhiteSpace(' ') + ANNOTATION_ENTRY + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('bar') + PsiWhiteSpace(' ') + ANNOTATION_ENTRY + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('baz') + PsiElement(RBRACKET)(']') + PsiWhiteSpace('\n') + PACKAGE_DIRECTIVE + PsiElement(package)('package') + PsiWhiteSpace(' ') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('bar') \ No newline at end of file diff --git a/compiler/testData/psi/annotation/onFile/nonFIleAnnotationBeforePackage.kt b/compiler/testData/psi/annotation/onFile/nonFIleAnnotationBeforePackage.kt new file mode 100644 index 00000000000..0510fdffe00 --- /dev/null +++ b/compiler/testData/psi/annotation/onFile/nonFIleAnnotationBeforePackage.kt @@ -0,0 +1,7 @@ +@foo +@file @bar +@file: +@:baz +@fil:ann +@ : +package boo diff --git a/compiler/testData/psi/annotation/onFile/nonFIleAnnotationBeforePackage.txt b/compiler/testData/psi/annotation/onFile/nonFIleAnnotationBeforePackage.txt new file mode 100644 index 00000000000..647ea19e7a2 --- /dev/null +++ b/compiler/testData/psi/annotation/onFile/nonFIleAnnotationBeforePackage.txt @@ -0,0 +1,60 @@ +JetFile: nonFIleAnnotationBeforePackage.kt + FILE_ANNOTATION_LIST + ANNOTATION_ENTRY + PsiElement(AT)('@') + PsiErrorElement:Expecting "file:" prefix for file annotations + + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('foo') + PsiWhiteSpace('\n') + ANNOTATION_ENTRY + PsiElement(AT)('@') + PsiElement(file)('file') + PsiWhiteSpace(' ') + PsiErrorElement:Expecting "file:" prefix for file annotations + PsiElement(AT)('@') + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('bar') + PsiWhiteSpace('\n') + PsiErrorElement:Expected annotation identifier after '@file:' + PsiElement(AT)('@') + PsiElement(IDENTIFIER)('file') + PsiElement(COLON)(':') + PsiWhiteSpace('\n') + ANNOTATION_ENTRY + PsiElement(AT)('@') + PsiErrorElement:Expected 'file' keyword before ':' + PsiElement(COLON)(':') + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('baz') + PsiWhiteSpace('\n') + ANNOTATION_ENTRY + PsiElement(AT)('@') + PsiErrorElement:Expected 'file' keyword as target + PsiElement(IDENTIFIER)('fil') + PsiElement(COLON)(':') + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('ann') + PsiWhiteSpace('\n') + PsiErrorElement:Expected annotation identifier after ':' + PsiElement(AT)('@') + PsiWhiteSpace(' ') + PsiElement(COLON)(':') + PsiWhiteSpace('\n') + PACKAGE_DIRECTIVE + PsiElement(package)('package') + PsiWhiteSpace(' ') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('boo') \ No newline at end of file diff --git a/compiler/testData/psi/annotation/onFile/single.kt b/compiler/testData/psi/annotation/onFile/single.kt new file mode 100644 index 00000000000..37019a40a7f --- /dev/null +++ b/compiler/testData/psi/annotation/onFile/single.kt @@ -0,0 +1,2 @@ +@file:foo +package bar diff --git a/compiler/testData/psi/annotation/onFile/single.txt b/compiler/testData/psi/annotation/onFile/single.txt new file mode 100644 index 00000000000..744a6ca7b58 --- /dev/null +++ b/compiler/testData/psi/annotation/onFile/single.txt @@ -0,0 +1,17 @@ +JetFile: single.kt + FILE_ANNOTATION_LIST + ANNOTATION_ENTRY + PsiElement(AT)('@') + PsiElement(file)('file') + PsiElement(COLON)(':') + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('foo') + PsiWhiteSpace('\n') + PACKAGE_DIRECTIVE + PsiElement(package)('package') + PsiWhiteSpace(' ') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('bar') \ No newline at end of file diff --git a/compiler/testData/psi/annotation/onFile/withoutFileAnnotationAndPackageDeclaration.kt b/compiler/testData/psi/annotation/onFile/withoutFileAnnotationAndPackageDeclaration.kt new file mode 100644 index 00000000000..e8193318906 --- /dev/null +++ b/compiler/testData/psi/annotation/onFile/withoutFileAnnotationAndPackageDeclaration.kt @@ -0,0 +1,3 @@ +@ann fun foo(): String? = null + +annotation class ann diff --git a/compiler/testData/psi/annotation/onFile/withoutFileAnnotationAndPackageDeclaration.txt b/compiler/testData/psi/annotation/onFile/withoutFileAnnotationAndPackageDeclaration.txt new file mode 100644 index 00000000000..e5b044602a4 --- /dev/null +++ b/compiler/testData/psi/annotation/onFile/withoutFileAnnotationAndPackageDeclaration.txt @@ -0,0 +1,40 @@ +JetFile: withoutFileAnnotationAndPackageDeclaration.kt + PACKAGE_DIRECTIVE + + FUN + MODIFIER_LIST + ANNOTATION_ENTRY + PsiElement(AT)('@') + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('ann') + PsiWhiteSpace(' ') + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('foo') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiElement(COLON)(':') + PsiWhiteSpace(' ') + TYPE_REFERENCE + NULLABLE_TYPE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('String') + PsiElement(QUEST)('?') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + NULL + PsiElement(null)('null') + PsiWhiteSpace('\n\n') + CLASS + MODIFIER_LIST + PsiElement(annotation)('annotation') + PsiWhiteSpace(' ') + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('ann') \ No newline at end of file diff --git a/compiler/testData/psi/annotation/onFile/withoutPackage.kt b/compiler/testData/psi/annotation/onFile/withoutPackage.kt new file mode 100644 index 00000000000..b4f7712d1f1 --- /dev/null +++ b/compiler/testData/psi/annotation/onFile/withoutPackage.kt @@ -0,0 +1,4 @@ +@file:foo +@foo @bar +@file:[baz] +fun foo() {} diff --git a/compiler/testData/psi/annotation/onFile/withoutPackage.txt b/compiler/testData/psi/annotation/onFile/withoutPackage.txt new file mode 100644 index 00000000000..b78d2c90ef9 --- /dev/null +++ b/compiler/testData/psi/annotation/onFile/withoutPackage.txt @@ -0,0 +1,56 @@ +JetFile: withoutPackage.kt + FILE_ANNOTATION_LIST + ANNOTATION_ENTRY + PsiElement(AT)('@') + PsiElement(file)('file') + PsiElement(COLON)(':') + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('foo') + PsiWhiteSpace('\n') + PACKAGE_DIRECTIVE + + FUN + MODIFIER_LIST + ANNOTATION_ENTRY + PsiElement(AT)('@') + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('foo') + PsiWhiteSpace(' ') + ANNOTATION_ENTRY + PsiElement(AT)('@') + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('bar') + PsiWhiteSpace('\n') + ANNOTATION + PsiElement(AT)('@') + PsiErrorElement:File annotations are only allowed before package declaration + PsiElement(file)('file') + PsiElement(COLON)(':') + PsiElement(LBRACKET)('[') + ANNOTATION_ENTRY + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('baz') + PsiElement(RBRACKET)(']') + PsiWhiteSpace('\n') + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('foo') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + BLOCK + PsiElement(LBRACE)('{') + PsiElement(RBRACE)('}') \ No newline at end of file diff --git a/compiler/testData/psi/annotation/onFile/withoutPackageWithSimpleAnnotation.kt b/compiler/testData/psi/annotation/onFile/withoutPackageWithSimpleAnnotation.kt new file mode 100644 index 00000000000..1555af1a3d0 --- /dev/null +++ b/compiler/testData/psi/annotation/onFile/withoutPackageWithSimpleAnnotation.kt @@ -0,0 +1,4 @@ +@file:foo +foo bar +@file: baz +fun foo() {} diff --git a/compiler/testData/psi/annotation/onFile/withoutPackageWithSimpleAnnotation.txt b/compiler/testData/psi/annotation/onFile/withoutPackageWithSimpleAnnotation.txt new file mode 100644 index 00000000000..acc7ad0983e --- /dev/null +++ b/compiler/testData/psi/annotation/onFile/withoutPackageWithSimpleAnnotation.txt @@ -0,0 +1,52 @@ +JetFile: withoutPackageWithSimpleAnnotation.kt + FILE_ANNOTATION_LIST + ANNOTATION_ENTRY + PsiElement(AT)('@') + PsiElement(file)('file') + PsiElement(COLON)(':') + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('foo') + PsiWhiteSpace('\n') + PACKAGE_DIRECTIVE + + FUN + MODIFIER_LIST + ANNOTATION_ENTRY + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('foo') + PsiWhiteSpace(' ') + ANNOTATION_ENTRY + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('bar') + PsiWhiteSpace('\n') + ANNOTATION_ENTRY + PsiElement(AT)('@') + PsiErrorElement:File annotations are only allowed before package declaration + PsiElement(file)('file') + PsiElement(COLON)(':') + PsiWhiteSpace(' ') + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('baz') + PsiWhiteSpace('\n') + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('foo') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + BLOCK + PsiElement(LBRACE)('{') + PsiElement(RBRACE)('}') \ No newline at end of file diff --git a/compiler/testData/psi/annotation/onFileObsolete/nonFIleAnnotationBeforePackage.txt b/compiler/testData/psi/annotation/onFileObsolete/nonFIleAnnotationBeforePackage.txt index dd774a94410..11b9215fb0d 100644 --- a/compiler/testData/psi/annotation/onFileObsolete/nonFIleAnnotationBeforePackage.txt +++ b/compiler/testData/psi/annotation/onFileObsolete/nonFIleAnnotationBeforePackage.txt @@ -49,9 +49,8 @@ JetFile: nonFIleAnnotationBeforePackage.kt PsiWhiteSpace('\n') ANNOTATION PsiElement(LBRACKET)('[') - PsiErrorElement:Expecting "file:" prefix for file annotations - - PsiElement(COLON)(':') + PsiErrorElement:Expected 'file' keyword before ':' + PsiElement(COLON)(':') ANNOTATION_ENTRY CONSTRUCTOR_CALLEE TYPE_REFERENCE @@ -74,9 +73,8 @@ JetFile: nonFIleAnnotationBeforePackage.kt PsiWhiteSpace('\n') ANNOTATION PsiElement(LBRACKET)('[') - PsiErrorElement:Expecting "file:" prefix for file annotations - - PsiElement(COLON)(':') + PsiErrorElement:Expected 'file' keyword before ':' + PsiElement(COLON)(':') PsiErrorElement:Expecting a list of annotations PsiElement(RBRACKET)(']') diff --git a/compiler/tests/org/jetbrains/kotlin/parsing/JetParsingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/parsing/JetParsingTestGenerated.java index 486a2da6742..08cebb73c44 100644 --- a/compiler/tests/org/jetbrains/kotlin/parsing/JetParsingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/parsing/JetParsingTestGenerated.java @@ -810,6 +810,84 @@ public class JetParsingTestGenerated extends AbstractJetParsingTest { } } + @TestMetadata("compiler/testData/psi/annotation/list") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class List extends AbstractJetParsingTest { + public void testAllFilesPresentInList() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/psi/annotation/list"), Pattern.compile("^(.*)\\.kts?$"), true); + } + + @TestMetadata("basic.kt") + public void testBasic() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/annotation/list/basic.kt"); + doParsingTest(fileName); + } + + @TestMetadata("recovery.kt") + public void testRecovery() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/annotation/list/recovery.kt"); + doParsingTest(fileName); + } + } + + @TestMetadata("compiler/testData/psi/annotation/onFile") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class OnFile extends AbstractJetParsingTest { + public void testAllFilesPresentInOnFile() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/psi/annotation/onFile"), Pattern.compile("^(.*)\\.kts?$"), true); + } + + @TestMetadata("fileAnnotationInWrongPlace.kt") + public void testFileAnnotationInWrongPlace() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/annotation/onFile/fileAnnotationInWrongPlace.kt"); + doParsingTest(fileName); + } + + @TestMetadata("manyAnnotationBlocks.kt") + public void testManyAnnotationBlocks() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/annotation/onFile/manyAnnotationBlocks.kt"); + doParsingTest(fileName); + } + + @TestMetadata("manyInOneAnnotationBlock.kt") + public void testManyInOneAnnotationBlock() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/annotation/onFile/manyInOneAnnotationBlock.kt"); + doParsingTest(fileName); + } + + @TestMetadata("nonFIleAnnotationBeforePackage.kt") + public void testNonFIleAnnotationBeforePackage() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/annotation/onFile/nonFIleAnnotationBeforePackage.kt"); + doParsingTest(fileName); + } + + @TestMetadata("single.kt") + public void testSingle() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/annotation/onFile/single.kt"); + doParsingTest(fileName); + } + + @TestMetadata("withoutFileAnnotationAndPackageDeclaration.kt") + public void testWithoutFileAnnotationAndPackageDeclaration() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/annotation/onFile/withoutFileAnnotationAndPackageDeclaration.kt"); + doParsingTest(fileName); + } + + @TestMetadata("withoutPackage.kt") + public void testWithoutPackage() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/annotation/onFile/withoutPackage.kt"); + doParsingTest(fileName); + } + + @TestMetadata("withoutPackageWithSimpleAnnotation.kt") + public void testWithoutPackageWithSimpleAnnotation() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/annotation/onFile/withoutPackageWithSimpleAnnotation.kt"); + doParsingTest(fileName); + } + } + @TestMetadata("compiler/testData/psi/annotation/onFileObsolete") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/grammar/src/attributes.grm b/grammar/src/attributes.grm index c149d9f33fb..37e9570adc8 100644 --- a/grammar/src/attributes.grm +++ b/grammar/src/attributes.grm @@ -7,10 +7,10 @@ annotations ; annotation - : "[" annotationEntry+ "]" - : annotationEntry + : "@"? "[" annotationEntry+ "]" + : "@"? annotationEntry ; annotationEntry - : "@"? ++ SimpleName{"."} typeArguments? valueArguments? + : SimpleName{"."} typeArguments? valueArguments? ; \ No newline at end of file diff --git a/grammar/src/toplevel.grm b/grammar/src/toplevel.grm index 922262d4823..920ece53cd9 100644 --- a/grammar/src/toplevel.grm +++ b/grammar/src/toplevel.grm @@ -24,7 +24,7 @@ fileAnnotations ; fileAnnotation - : "[" "file" ":" annotationEntry+ "]" + : "@" "file" ":" ("[" annotationEntry+ "]" | annotationEntry) ; packageHeader