diff --git a/compiler/frontend/src/org/jetbrains/kotlin/parsing/JetExpressionParsing.java b/compiler/frontend/src/org/jetbrains/kotlin/parsing/JetExpressionParsing.java index 76407e8783b..6fab4b4a006 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/parsing/JetExpressionParsing.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/parsing/JetExpressionParsing.java @@ -35,6 +35,7 @@ import static org.jetbrains.kotlin.JetNodeTypes.*; import static org.jetbrains.kotlin.lexer.JetTokens.*; import static org.jetbrains.kotlin.parsing.JetParsing.AnnotationParsingMode.ALLOW_UNESCAPED_REGULAR_ANNOTATIONS; import static org.jetbrains.kotlin.parsing.JetParsing.AnnotationParsingMode.ONLY_ESCAPED_REGULAR_ANNOTATIONS; +import static org.jetbrains.kotlin.parsing.JetParsing.DeclarationParsingMode.LOCAL; public class JetExpressionParsing extends AbstractJetParsing { private static final TokenSet WHEN_CONDITION_RECOVERY_SET = TokenSet.create(RBRACE, IN_KEYWORD, NOT_IN, IS_KEYWORD, NOT_IS, ELSE_KEYWORD); @@ -1376,7 +1377,7 @@ public class JetExpressionParsing extends AbstractJetParsing { IElementType keywordToken = tt(); IElementType declType = null; if (keywordToken == CLASS_KEYWORD || keywordToken == TRAIT_KEYWORD || keywordToken == INTERFACE_KEYWORD) { - declType = myJetParsing.parseClass(isEnum); + declType = myJetParsing.parseClass(isEnum, LOCAL); } else if (keywordToken == FUN_KEYWORD) { declType = myJetParsing.parseFunction(); @@ -1400,7 +1401,7 @@ public class JetExpressionParsing extends AbstractJetParsing { return null; } - myJetParsing.parseObject(NameParsingMode.REQUIRED, true); + myJetParsing.parseObject(NameParsingMode.REQUIRED, true, LOCAL); declType = OBJECT_DECLARATION; } return declType; @@ -1934,7 +1935,7 @@ public class JetExpressionParsing extends AbstractJetParsing { public void parseObjectLiteral() { PsiBuilder.Marker literal = mark(); PsiBuilder.Marker declaration = mark(); - myJetParsing.parseObject(NameParsingMode.PROHIBITED, false); // Body is not optional because of foo(object : A, B) + myJetParsing.parseObject(NameParsingMode.PROHIBITED, false, LOCAL); // Body is not optional because of foo(object : A, B) declaration.done(OBJECT_DECLARATION); literal.done(OBJECT_LITERAL); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/parsing/JetParsing.java b/compiler/frontend/src/org/jetbrains/kotlin/parsing/JetParsing.java index 9792c947e44..efa7c34f509 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/parsing/JetParsing.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/parsing/JetParsing.java @@ -32,6 +32,7 @@ import java.util.Map; import static org.jetbrains.kotlin.JetNodeTypes.*; import static org.jetbrains.kotlin.lexer.JetTokens.*; import static org.jetbrains.kotlin.parsing.JetParsing.AnnotationParsingMode.*; +import static org.jetbrains.kotlin.parsing.JetParsing.DeclarationParsingMode.*; public class JetParsing extends AbstractJetParsing { private static final Logger LOG = Logger.getInstance(JetParsing.class); @@ -387,7 +388,7 @@ public class JetParsing extends AbstractJetParsing { // } // else if (keywordToken == CLASS_KEYWORD || keywordToken == TRAIT_KEYWORD || keywordToken == INTERFACE_KEYWORD) { - declType = parseClass(detector.isEnumDetected()); + declType = parseClass(detector.isEnumDetected(), TOP_LEVEL); } else if (keywordToken == FUN_KEYWORD) { declType = parseFunction(); @@ -399,7 +400,7 @@ public class JetParsing extends AbstractJetParsing { declType = parseTypeAlias(); } else if (keywordToken == OBJECT_KEYWORD) { - parseObject(NameParsingMode.REQUIRED, true); + parseObject(NameParsingMode.REQUIRED, true, TOP_LEVEL); declType = OBJECT_DECLARATION; } else if (at(LBRACE)) { @@ -439,7 +440,8 @@ public class JetParsing extends AbstractJetParsing { boolean empty = true; while (!eof()) { if (annotationParsingMode.atMemberStart && atSet(SOFT_KEYWORDS_AT_MEMBER_START)) break; - if (annotationParsingMode == PRIMARY_CONSTRUCTOR_MODIFIER_LIST && atSet(CONSTRUCTOR_KEYWORD, WHERE_KEYWORD)) break; + if ((annotationParsingMode == PRIMARY_CONSTRUCTOR_MODIFIER_LIST || annotationParsingMode == PRIMARY_CONSTRUCTOR_MODIFIER_LIST_LOCAL) && + atSet(CONSTRUCTOR_KEYWORD, WHERE_KEYWORD)) break; if (at(AT)) { if (!tryParseModifier(tokenConsumer)) { @@ -693,6 +695,12 @@ public class JetParsing extends AbstractJetParsing { PROHIBITED } + public enum DeclarationParsingMode { + TOP_LEVEL, + CLASS_MEMBER, + LOCAL + } + /* * class * : modifiers ("class" | "interface") SimpleName @@ -707,7 +715,13 @@ public class JetParsing extends AbstractJetParsing { * : "object" SimpleName? ":" delegationSpecifier{","}? classBody? * ; */ - IElementType parseClassOrObject(boolean object, NameParsingMode nameParsingMode, boolean optionalBody, boolean enumClass) { + IElementType parseClassOrObject( + boolean object, + NameParsingMode nameParsingMode, + boolean optionalBody, + boolean enumClass, + DeclarationParsingMode declarationParsingMode + ) { if (object) { assert _at(OBJECT_KEYWORD); } @@ -743,7 +757,9 @@ public class JetParsing extends AbstractJetParsing { OptionalMarker constructorModifiersMarker = new OptionalMarker(object); PsiBuilder.Marker beforeConstructorModifiers = mark(); PsiBuilder.Marker primaryConstructorMarker = mark(); - boolean hasConstructorModifiers = parseModifierList(PRIMARY_CONSTRUCTOR_MODIFIER_LIST); + boolean hasConstructorModifiers = parseModifierList( + declarationParsingMode != LOCAL ? PRIMARY_CONSTRUCTOR_MODIFIER_LIST : PRIMARY_CONSTRUCTOR_MODIFIER_LIST_LOCAL + ); // Some modifiers found, but no parentheses following: class has already ended, and we are looking at something else if ((object && at(CONSTRUCTOR_KEYWORD)) || (hasConstructorModifiers && !atSet(LPAR, LBRACE, COLON, CONSTRUCTOR_KEYWORD))) { @@ -808,12 +824,12 @@ public class JetParsing extends AbstractJetParsing { return object ? OBJECT_DECLARATION : CLASS; } - IElementType parseClass(boolean enumClass) { - return parseClassOrObject(false, NameParsingMode.REQUIRED, true, enumClass); + IElementType parseClass(boolean enumClass, DeclarationParsingMode declarationParsingMode) { + return parseClassOrObject(false, NameParsingMode.REQUIRED, true, enumClass, declarationParsingMode); } - void parseObject(NameParsingMode nameParsingMode, boolean optionalBody) { - parseClassOrObject(true, nameParsingMode, optionalBody, false); + void parseObject(NameParsingMode nameParsingMode, boolean optionalBody, DeclarationParsingMode declarationParsingMode) { + parseClassOrObject(true, nameParsingMode, optionalBody, false, declarationParsingMode); } /* @@ -1023,7 +1039,7 @@ public class JetParsing extends AbstractJetParsing { IElementType keywordToken = tt(); IElementType declType = null; if (keywordToken == CLASS_KEYWORD || keywordToken == TRAIT_KEYWORD || keywordToken == INTERFACE_KEYWORD) { - declType = parseClass(isEnum); + declType = parseClass(isEnum, CLASS_MEMBER); } else if (keywordToken == FUN_KEYWORD) { declType = parseFunction(); @@ -1035,7 +1051,7 @@ public class JetParsing extends AbstractJetParsing { declType = parseTypeAlias(); } else if (keywordToken == OBJECT_KEYWORD) { - parseObject(isDefault ? NameParsingMode.ALLOWED : NameParsingMode.REQUIRED, true); + parseObject(isDefault ? NameParsingMode.ALLOWED : NameParsingMode.REQUIRED, true, CLASS_MEMBER); declType = OBJECT_DECLARATION; } else if (at(INIT_KEYWORD)) { @@ -2313,7 +2329,8 @@ public class JetParsing extends AbstractJetParsing { ONLY_ESCAPED_REGULAR_ANNOTATIONS(false, false, false), ALLOW_UNESCAPED_REGULAR_ANNOTATIONS(true, false, false), ALLOW_UNESCAPED_REGULAR_ANNOTATIONS_AT_MEMBER_MODIFIER_LIST(true, false, true), - PRIMARY_CONSTRUCTOR_MODIFIER_LIST(true, false, false); + PRIMARY_CONSTRUCTOR_MODIFIER_LIST(true, false, false), + PRIMARY_CONSTRUCTOR_MODIFIER_LIST_LOCAL(false, false, false); boolean allowShortAnnotations; boolean isFileAnnotationParsingMode; diff --git a/compiler/testData/psi/primaryConstructor/local.kt b/compiler/testData/psi/primaryConstructor/local.kt index ac8a8f54523..afd190f544f 100644 --- a/compiler/testData/psi/primaryConstructor/local.kt +++ b/compiler/testData/psi/primaryConstructor/local.kt @@ -1,4 +1,15 @@ fun foo() { class A1 constructor() class A2 Ann private constructor() + + class A3 private @Ann("") constructor() + class A4 @Ann("") constructor() + + class A5 + Ann + constructor() + + class A6 + Ann("") + constructor() } diff --git a/compiler/testData/psi/primaryConstructor/local.txt b/compiler/testData/psi/primaryConstructor/local.txt index ea17423ede4..b3dd62dfbd8 100644 --- a/compiler/testData/psi/primaryConstructor/local.txt +++ b/compiler/testData/psi/primaryConstructor/local.txt @@ -27,21 +27,108 @@ JetFile: local.kt PsiElement(class)('class') PsiWhiteSpace(' ') PsiElement(IDENTIFIER)('A2') + PsiWhiteSpace(' ') + PsiErrorElement:Unexpected tokens (use ';' to separate expressions on the same line) + PsiElement(IDENTIFIER)('Ann') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('private') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('constructor') + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n\n ') + CLASS + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('A3') PsiWhiteSpace(' ') PRIMARY_CONSTRUCTOR MODIFIER_LIST + PsiElement(private)('private') + PsiWhiteSpace(' ') ANNOTATION_ENTRY + PsiElement(AT)('@') CONSTRUCTOR_CALLEE TYPE_REFERENCE USER_TYPE REFERENCE_EXPRESSION PsiElement(IDENTIFIER)('Ann') - PsiWhiteSpace(' ') - PsiElement(private)('private') + VALUE_ARGUMENT_LIST + PsiElement(LPAR)('(') + VALUE_ARGUMENT + STRING_TEMPLATE + PsiElement(OPEN_QUOTE)('"') + PsiElement(CLOSING_QUOTE)('"') + PsiElement(RPAR)(')') PsiWhiteSpace(' ') PsiElement(constructor)('constructor') VALUE_PARAMETER_LIST PsiElement(LPAR)('(') PsiElement(RPAR)(')') + PsiWhiteSpace('\n ') + CLASS + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('A4') + PsiWhiteSpace(' ') + PRIMARY_CONSTRUCTOR + MODIFIER_LIST + ANNOTATION_ENTRY + PsiElement(AT)('@') + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('Ann') + VALUE_ARGUMENT_LIST + PsiElement(LPAR)('(') + VALUE_ARGUMENT + STRING_TEMPLATE + PsiElement(OPEN_QUOTE)('"') + PsiElement(CLOSING_QUOTE)('"') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + PsiElement(constructor)('constructor') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n\n ') + CLASS + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('A5') + PsiWhiteSpace('\n ') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('Ann') + PsiWhiteSpace('\n ') + CALL_EXPRESSION + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('constructor') + VALUE_ARGUMENT_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n\n ') + CLASS + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('A6') + PsiWhiteSpace('\n ') + CALL_EXPRESSION + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('Ann') + VALUE_ARGUMENT_LIST + PsiElement(LPAR)('(') + VALUE_ARGUMENT + STRING_TEMPLATE + PsiElement(OPEN_QUOTE)('"') + PsiElement(CLOSING_QUOTE)('"') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n ') + CALL_EXPRESSION + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('constructor') + VALUE_ARGUMENT_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') PsiWhiteSpace('\n') PsiElement(RBRACE)('}') \ No newline at end of file