diff --git a/compiler/frontend/src/org/jetbrains/kotlin/parsing/JetExpressionParsing.java b/compiler/frontend/src/org/jetbrains/kotlin/parsing/JetExpressionParsing.java index f89012348a1..53ec1f993f6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/parsing/JetExpressionParsing.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/parsing/JetExpressionParsing.java @@ -423,9 +423,13 @@ public class JetExpressionParsing extends AbstractJetParsing { private void parsePostfixExpression() { PsiBuilder.Marker expression = mark(); + boolean firstExpressionParsed; boolean doubleColonExpression = parseDoubleColonExpression(); if (!doubleColonExpression) { - parseAtomicExpression(); + firstExpressionParsed = parseAtomicExpression(); + } + else { + firstExpressionParsed = true; } while (true) { @@ -439,19 +443,24 @@ public class JetExpressionParsing extends AbstractJetParsing { else if (!doubleColonExpression && parseCallSuffix()) { expression.done(CALL_EXPRESSION); } - else if (at(DOT)) { - advance(); // DOT + else if (at(DOT) || at(SAFE_ACCESS)) { + IElementType expressionType = at(DOT) ? DOT_QUALIFIED_EXPRESSION : SAFE_ACCESS_EXPRESSION; + advance(); // DOT or SAFE_ACCESS + + if (!firstExpressionParsed) { + expression.drop(); + expression = mark(); + } parseCallExpression(); - expression.done(DOT_QUALIFIED_EXPRESSION); - } - else if (at(SAFE_ACCESS)) { - advance(); // SAFE_ACCESS - - parseCallExpression(); - - expression.done(SAFE_ACCESS_EXPRESSION); + if (firstExpressionParsed) { + expression.done(expressionType); + } + else { + firstExpressionParsed = true; + continue; + } } else if (atSet(Precedence.POSTFIX.getOperations())) { parseOperationReference(); @@ -557,9 +566,11 @@ public class JetExpressionParsing extends AbstractJetParsing { * : "package" // for the root package * ; */ - private void parseAtomicExpression() { + private boolean parseAtomicExpression() { // System.out.println("atom at " + myBuilder.getTokenText()); + boolean ok = true; + if (at(LPAR)) { parseParenthesizedExpression(); } @@ -625,9 +636,12 @@ public class JetExpressionParsing extends AbstractJetParsing { parseStringTemplate(); } else if (!parseLiteralConstant()) { + ok = false; // TODO: better recovery if FIRST(element) did not match errorWithRecovery("Expecting an element", EXPRESSION_FOLLOW); } + + return ok; } /* diff --git a/compiler/testData/psi/recovery/qualifiedExpression/noQualifiedExpression.kt b/compiler/testData/psi/recovery/qualifiedExpression/noQualifiedExpression.kt new file mode 100644 index 00000000000..99d0e3c15cf --- /dev/null +++ b/compiler/testData/psi/recovery/qualifiedExpression/noQualifiedExpression.kt @@ -0,0 +1,3 @@ +fun test() { + MyClass.Companion.test +} \ No newline at end of file diff --git a/compiler/testData/psi/recovery/qualifiedExpression/noQualifiedExpression.txt b/compiler/testData/psi/recovery/qualifiedExpression/noQualifiedExpression.txt new file mode 100644 index 00000000000..87e578a50f0 --- /dev/null +++ b/compiler/testData/psi/recovery/qualifiedExpression/noQualifiedExpression.txt @@ -0,0 +1,52 @@ +JetFile: noQualifiedExpression.kt + PACKAGE_DIRECTIVE + + FUN + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('test') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + BLOCK + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + BINARY_EXPRESSION + BINARY_EXPRESSION + DOT_QUALIFIED_EXPRESSION + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('MyClass') + PsiElement(DOT)('.') + PsiErrorElement:Expecting an element + PsiElement(LT)('<') + OPERATION_REFERENCE + PsiElement(IDENTIFIER)('warning') + PsiWhiteSpace(' ') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('descr') + OPERATION_REFERENCE + PsiElement(EQ)('=') + BINARY_EXPRESSION + BINARY_EXPRESSION + STRING_TEMPLATE + PsiElement(OPEN_QUOTE)('"') + PsiElement(CLOSING_QUOTE)('"') + OPERATION_REFERENCE + PsiElement(GT)('>') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('Companion') + OPERATION_REFERENCE + PsiElement(LT)('<') + BINARY_EXPRESSION + PsiErrorElement:Expecting an element + PsiElement(DIV)('/') + OPERATION_REFERENCE + PsiElement(IDENTIFIER)('warning') + PsiErrorElement:Expecting an element + PsiElement(GT)('>') + PsiElement(DOT)('.') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('test') + PsiWhiteSpace('\n') + PsiElement(RBRACE)('}') \ No newline at end of file diff --git a/compiler/testData/psi/recovery/qualifiedExpression/noSafeQualifiedExpression.kt b/compiler/testData/psi/recovery/qualifiedExpression/noSafeQualifiedExpression.kt new file mode 100644 index 00000000000..528f5b19bba --- /dev/null +++ b/compiler/testData/psi/recovery/qualifiedExpression/noSafeQualifiedExpression.kt @@ -0,0 +1,3 @@ +fun test() { + MyClass.Companion?.test +} \ No newline at end of file diff --git a/compiler/testData/psi/recovery/qualifiedExpression/noSafeQualifiedExpression.txt b/compiler/testData/psi/recovery/qualifiedExpression/noSafeQualifiedExpression.txt new file mode 100644 index 00000000000..9fd500624d8 --- /dev/null +++ b/compiler/testData/psi/recovery/qualifiedExpression/noSafeQualifiedExpression.txt @@ -0,0 +1,52 @@ +JetFile: noSafeQualifiedExpression.kt + PACKAGE_DIRECTIVE + + FUN + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('test') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + BLOCK + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + BINARY_EXPRESSION + BINARY_EXPRESSION + DOT_QUALIFIED_EXPRESSION + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('MyClass') + PsiElement(DOT)('.') + PsiErrorElement:Expecting an element + PsiElement(LT)('<') + OPERATION_REFERENCE + PsiElement(IDENTIFIER)('warning') + PsiWhiteSpace(' ') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('descr') + OPERATION_REFERENCE + PsiElement(EQ)('=') + BINARY_EXPRESSION + BINARY_EXPRESSION + STRING_TEMPLATE + PsiElement(OPEN_QUOTE)('"') + PsiElement(CLOSING_QUOTE)('"') + OPERATION_REFERENCE + PsiElement(GT)('>') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('Companion') + OPERATION_REFERENCE + PsiElement(LT)('<') + BINARY_EXPRESSION + PsiErrorElement:Expecting an element + PsiElement(DIV)('/') + OPERATION_REFERENCE + PsiElement(IDENTIFIER)('warning') + PsiErrorElement:Expecting an element + PsiElement(GT)('>') + PsiElement(SAFE_ACCESS)('?.') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('test') + PsiWhiteSpace('\n') + PsiElement(RBRACE)('}') \ No newline at end of file diff --git a/compiler/testData/psi/recovery/qualifiedExpression/qualifiedExpressionWithSelector.kt b/compiler/testData/psi/recovery/qualifiedExpression/qualifiedExpressionWithSelector.kt new file mode 100644 index 00000000000..41329e65a98 --- /dev/null +++ b/compiler/testData/psi/recovery/qualifiedExpression/qualifiedExpressionWithSelector.kt @@ -0,0 +1,3 @@ +fun test() { + MyClass.Companion.test.test +} \ No newline at end of file diff --git a/compiler/testData/psi/recovery/qualifiedExpression/qualifiedExpressionWithSelector.txt b/compiler/testData/psi/recovery/qualifiedExpression/qualifiedExpressionWithSelector.txt new file mode 100644 index 00000000000..38eda61fbfd --- /dev/null +++ b/compiler/testData/psi/recovery/qualifiedExpression/qualifiedExpressionWithSelector.txt @@ -0,0 +1,56 @@ +JetFile: qualifiedExpressionWithSelector.kt + PACKAGE_DIRECTIVE + + FUN + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('test') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + BLOCK + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + BINARY_EXPRESSION + BINARY_EXPRESSION + DOT_QUALIFIED_EXPRESSION + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('MyClass') + PsiElement(DOT)('.') + PsiErrorElement:Expecting an element + PsiElement(LT)('<') + OPERATION_REFERENCE + PsiElement(IDENTIFIER)('warning') + PsiWhiteSpace(' ') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('descr') + OPERATION_REFERENCE + PsiElement(EQ)('=') + BINARY_EXPRESSION + BINARY_EXPRESSION + STRING_TEMPLATE + PsiElement(OPEN_QUOTE)('"') + PsiElement(CLOSING_QUOTE)('"') + OPERATION_REFERENCE + PsiElement(GT)('>') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('Companion') + OPERATION_REFERENCE + PsiElement(LT)('<') + BINARY_EXPRESSION + PsiErrorElement:Expecting an element + PsiElement(DIV)('/') + OPERATION_REFERENCE + PsiElement(IDENTIFIER)('warning') + PsiErrorElement:Expecting an element + PsiElement(GT)('>') + PsiElement(DOT)('.') + DOT_QUALIFIED_EXPRESSION + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('test') + PsiElement(DOT)('.') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('test') + PsiWhiteSpace('\n') + PsiElement(RBRACE)('}') \ No newline at end of file diff --git a/compiler/testData/psi/recovery/qualifiedExpression/qualifiedExpressionWithoutSelector.kt b/compiler/testData/psi/recovery/qualifiedExpression/qualifiedExpressionWithoutSelector.kt new file mode 100644 index 00000000000..3fd0db283af --- /dev/null +++ b/compiler/testData/psi/recovery/qualifiedExpression/qualifiedExpressionWithoutSelector.kt @@ -0,0 +1,3 @@ +fun test() { + MyClass.Companion.test. +} \ No newline at end of file diff --git a/compiler/testData/psi/recovery/qualifiedExpression/qualifiedExpressionWithoutSelector.txt b/compiler/testData/psi/recovery/qualifiedExpression/qualifiedExpressionWithoutSelector.txt new file mode 100644 index 00000000000..fe767c56290 --- /dev/null +++ b/compiler/testData/psi/recovery/qualifiedExpression/qualifiedExpressionWithoutSelector.txt @@ -0,0 +1,61 @@ +JetFile: qualifiedExpressionWithoutSelector.kt + PACKAGE_DIRECTIVE + + FUN + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('test') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + BLOCK + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + BINARY_EXPRESSION + BINARY_EXPRESSION + DOT_QUALIFIED_EXPRESSION + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('MyClass') + PsiElement(DOT)('.') + PsiErrorElement:Expecting an element + PsiElement(LT)('<') + OPERATION_REFERENCE + PsiElement(IDENTIFIER)('warning') + PsiWhiteSpace(' ') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('descr') + OPERATION_REFERENCE + PsiElement(EQ)('=') + BINARY_EXPRESSION + BINARY_EXPRESSION + STRING_TEMPLATE + PsiElement(OPEN_QUOTE)('"') + PsiElement(CLOSING_QUOTE)('"') + OPERATION_REFERENCE + PsiElement(GT)('>') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('Companion') + OPERATION_REFERENCE + PsiElement(LT)('<') + BINARY_EXPRESSION + BINARY_EXPRESSION + PsiErrorElement:Expecting an element + PsiElement(DIV)('/') + OPERATION_REFERENCE + PsiElement(IDENTIFIER)('warning') + PsiErrorElement:Expecting an element + PsiElement(GT)('>') + PsiElement(DOT)('.') + DOT_QUALIFIED_EXPRESSION + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('test') + PsiElement(DOT)('.') + PsiErrorElement:Expecting an element + PsiElement(LT)('<') + OPERATION_REFERENCE + PsiElement(IDENTIFIER)('ff') + PsiErrorElement:Expecting an element + PsiElement(GT)('>') + PsiWhiteSpace('\n') + PsiElement(RBRACE)('}') \ No newline at end of file diff --git a/compiler/testData/psi/recovery/qualifiedExpression/safeQualifiedExpressionWithSelector.kt b/compiler/testData/psi/recovery/qualifiedExpression/safeQualifiedExpressionWithSelector.kt new file mode 100644 index 00000000000..799014adcb3 --- /dev/null +++ b/compiler/testData/psi/recovery/qualifiedExpression/safeQualifiedExpressionWithSelector.kt @@ -0,0 +1,3 @@ +fun test() { + MyClass.Companion?.test?.test +} \ No newline at end of file diff --git a/compiler/testData/psi/recovery/qualifiedExpression/safeQualifiedExpressionWithSelector.txt b/compiler/testData/psi/recovery/qualifiedExpression/safeQualifiedExpressionWithSelector.txt new file mode 100644 index 00000000000..3fc68b57feb --- /dev/null +++ b/compiler/testData/psi/recovery/qualifiedExpression/safeQualifiedExpressionWithSelector.txt @@ -0,0 +1,56 @@ +JetFile: safeQualifiedExpressionWithSelector.kt + PACKAGE_DIRECTIVE + + FUN + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('test') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + BLOCK + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + BINARY_EXPRESSION + BINARY_EXPRESSION + DOT_QUALIFIED_EXPRESSION + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('MyClass') + PsiElement(DOT)('.') + PsiErrorElement:Expecting an element + PsiElement(LT)('<') + OPERATION_REFERENCE + PsiElement(IDENTIFIER)('warning') + PsiWhiteSpace(' ') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('descr') + OPERATION_REFERENCE + PsiElement(EQ)('=') + BINARY_EXPRESSION + BINARY_EXPRESSION + STRING_TEMPLATE + PsiElement(OPEN_QUOTE)('"') + PsiElement(CLOSING_QUOTE)('"') + OPERATION_REFERENCE + PsiElement(GT)('>') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('Companion') + OPERATION_REFERENCE + PsiElement(LT)('<') + BINARY_EXPRESSION + PsiErrorElement:Expecting an element + PsiElement(DIV)('/') + OPERATION_REFERENCE + PsiElement(IDENTIFIER)('warning') + PsiErrorElement:Expecting an element + PsiElement(GT)('>') + PsiElement(SAFE_ACCESS)('?.') + SAFE_ACCESS_EXPRESSION + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('test') + PsiElement(SAFE_ACCESS)('?.') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('test') + PsiWhiteSpace('\n') + PsiElement(RBRACE)('}') \ No newline at end of file diff --git a/compiler/testData/psi/recovery/qualifiedExpression/safeQualifiedExpressionWithoutSelector.kt b/compiler/testData/psi/recovery/qualifiedExpression/safeQualifiedExpressionWithoutSelector.kt new file mode 100644 index 00000000000..3796c3e3099 --- /dev/null +++ b/compiler/testData/psi/recovery/qualifiedExpression/safeQualifiedExpressionWithoutSelector.kt @@ -0,0 +1,3 @@ +fun test() { + MyClass.Companion?.test?. +} \ No newline at end of file diff --git a/compiler/testData/psi/recovery/qualifiedExpression/safeQualifiedExpressionWithoutSelector.txt b/compiler/testData/psi/recovery/qualifiedExpression/safeQualifiedExpressionWithoutSelector.txt new file mode 100644 index 00000000000..31a8d330739 --- /dev/null +++ b/compiler/testData/psi/recovery/qualifiedExpression/safeQualifiedExpressionWithoutSelector.txt @@ -0,0 +1,61 @@ +JetFile: safeQualifiedExpressionWithoutSelector.kt + PACKAGE_DIRECTIVE + + FUN + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('test') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + BLOCK + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + BINARY_EXPRESSION + BINARY_EXPRESSION + DOT_QUALIFIED_EXPRESSION + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('MyClass') + PsiElement(DOT)('.') + PsiErrorElement:Expecting an element + PsiElement(LT)('<') + OPERATION_REFERENCE + PsiElement(IDENTIFIER)('warning') + PsiWhiteSpace(' ') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('descr') + OPERATION_REFERENCE + PsiElement(EQ)('=') + BINARY_EXPRESSION + BINARY_EXPRESSION + STRING_TEMPLATE + PsiElement(OPEN_QUOTE)('"') + PsiElement(CLOSING_QUOTE)('"') + OPERATION_REFERENCE + PsiElement(GT)('>') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('Companion') + OPERATION_REFERENCE + PsiElement(LT)('<') + BINARY_EXPRESSION + BINARY_EXPRESSION + PsiErrorElement:Expecting an element + PsiElement(DIV)('/') + OPERATION_REFERENCE + PsiElement(IDENTIFIER)('warning') + PsiErrorElement:Expecting an element + PsiElement(GT)('>') + PsiElement(SAFE_ACCESS)('?.') + SAFE_ACCESS_EXPRESSION + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('test') + PsiElement(SAFE_ACCESS)('?.') + PsiErrorElement:Expecting an element + PsiElement(LT)('<') + OPERATION_REFERENCE + PsiElement(IDENTIFIER)('ff') + PsiErrorElement:Expecting an element + PsiElement(GT)('>') + PsiWhiteSpace('\n') + PsiElement(RBRACE)('}') \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/parsing/JetParsingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/parsing/JetParsingTestGenerated.java index dc49a29db6d..db90b2b720f 100644 --- a/compiler/tests/org/jetbrains/kotlin/parsing/JetParsingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/parsing/JetParsingTestGenerated.java @@ -1455,6 +1455,7 @@ public class JetParsingTestGenerated extends AbstractJetParsingTest { @TestDataPath("$PROJECT_ROOT") @InnerTestClasses({ Recovery.Objects.class, + Recovery.QualifiedExpression.class, }) @RunWith(JUnit3RunnerWithInners.class) public static class Recovery extends AbstractJetParsingTest { @@ -1798,6 +1799,51 @@ public class JetParsingTestGenerated extends AbstractJetParsingTest { } } } + + @TestMetadata("compiler/testData/psi/recovery/qualifiedExpression") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class QualifiedExpression extends AbstractJetParsingTest { + public void testAllFilesPresentInQualifiedExpression() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/psi/recovery/qualifiedExpression"), Pattern.compile("^(.*)\\.kts?$"), true); + } + + @TestMetadata("noQualifiedExpression.kt") + public void testNoQualifiedExpression() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/recovery/qualifiedExpression/noQualifiedExpression.kt"); + doParsingTest(fileName); + } + + @TestMetadata("noSafeQualifiedExpression.kt") + public void testNoSafeQualifiedExpression() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/recovery/qualifiedExpression/noSafeQualifiedExpression.kt"); + doParsingTest(fileName); + } + + @TestMetadata("qualifiedExpressionWithSelector.kt") + public void testQualifiedExpressionWithSelector() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/recovery/qualifiedExpression/qualifiedExpressionWithSelector.kt"); + doParsingTest(fileName); + } + + @TestMetadata("qualifiedExpressionWithoutSelector.kt") + public void testQualifiedExpressionWithoutSelector() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/recovery/qualifiedExpression/qualifiedExpressionWithoutSelector.kt"); + doParsingTest(fileName); + } + + @TestMetadata("safeQualifiedExpressionWithSelector.kt") + public void testSafeQualifiedExpressionWithSelector() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/recovery/qualifiedExpression/safeQualifiedExpressionWithSelector.kt"); + doParsingTest(fileName); + } + + @TestMetadata("safeQualifiedExpressionWithoutSelector.kt") + public void testSafeQualifiedExpressionWithoutSelector() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/recovery/qualifiedExpression/safeQualifiedExpressionWithoutSelector.kt"); + doParsingTest(fileName); + } + } } @TestMetadata("compiler/testData/psi/script")