From 4725dd30289f4123c74549fb3f4062e239245889 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Wed, 13 Jul 2016 18:32:47 +0300 Subject: [PATCH] Implement recovery for incomplete expression before declaration It's needed when declarations are parsed as a part of previous expression (see tests) Currently we apply this kind of recovery in a conservative way, only when declaration starts at the next line, and while the condition could be relaxed, there's no need to do this #KT-4948 Fixed #KT-7118 Fixed --- .../parsing/KotlinExpressionParsing.java | 38 +-- .../kotlin/parsing/KotlinParsing.java | 22 +- .../namedFunAsLastExpressionInBlock.kt | 12 +- .../declarationAfterDotSelectorExpected.kt | 29 +++ .../declarationAfterDotSelectorExpected.txt | 18 ++ .../declarationAfterIncompleteElvis.kt | 22 ++ .../declarationAfterIncompleteElvis.txt | 15 ++ .../variableDeclarationInSelector.kt | 6 +- .../DeclarationAfterDotSelectorExpected.kt | 27 ++ .../DeclarationAfterDotSelectorExpected.txt | 231 ++++++++++++++++++ .../DeclarationAfterIncompleteElvis.kt | 21 ++ .../DeclarationAfterIncompleteElvis.txt | 188 ++++++++++++++ .../checkers/DiagnosticsTestGenerated.java | 12 + .../kotlin/parsing/ParsingTestGenerated.java | 12 + 14 files changed, 623 insertions(+), 30 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/declarationAfterDotSelectorExpected.kt create mode 100644 compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/declarationAfterDotSelectorExpected.txt create mode 100644 compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/declarationAfterIncompleteElvis.kt create mode 100644 compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/declarationAfterIncompleteElvis.txt create mode 100644 compiler/testData/psi/recovery/DeclarationAfterDotSelectorExpected.kt create mode 100644 compiler/testData/psi/recovery/DeclarationAfterDotSelectorExpected.txt create mode 100644 compiler/testData/psi/recovery/DeclarationAfterIncompleteElvis.kt create mode 100644 compiler/testData/psi/recovery/DeclarationAfterIncompleteElvis.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinExpressionParsing.java b/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinExpressionParsing.java index 72bf900a8a8..05148409d4e 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinExpressionParsing.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinExpressionParsing.java @@ -21,6 +21,7 @@ import com.intellij.lang.PsiBuilder; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.KtNodeType; import org.jetbrains.kotlin.KtNodeTypes; import org.jetbrains.kotlin.lexer.KtToken; @@ -42,6 +43,9 @@ public class KotlinExpressionParsing extends AbstractKotlinParsing { private static final ImmutableMap KEYWORD_TEXTS = tokenSetToMap(KEYWORDS); + private static final IElementType[] LOCAL_DECLARATION_FIRST = + new IElementType[] {CLASS_KEYWORD, INTERFACE_KEYWORD, FUN_KEYWORD, VAL_KEYWORD, VAR_KEYWORD, TYPE_ALIAS_KEYWORD}; + private static ImmutableMap tokenSetToMap(TokenSet tokens) { ImmutableMap.Builder builder = ImmutableMap.builder(); for (IElementType token : tokens.getTypes()) { @@ -298,8 +302,6 @@ public class KotlinExpressionParsing extends AbstractKotlinParsing { * see the precedence table */ private void parseBinaryExpression(Precedence precedence) { - // System.out.println(precedence.name() + " at " + myBuilder.getTokenText()); - PsiBuilder.Marker expression = mark(); precedence.parseHigherPrecedence(this); @@ -331,10 +333,8 @@ public class KotlinExpressionParsing extends AbstractKotlinParsing { * operation? prefixExpression */ private void parsePrefixExpression() { - // System.out.println("pre at " + myBuilder.getTokenText()); - if (at(AT)) { - if (!parseLocalDeclaration()) { + if (!parseLocalDeclaration(/* rollbackIfDefinitelyNotExpression = */ false)) { PsiBuilder.Marker expression = mark(); myKotlinParsing.parseAnnotations(DEFAULT); parsePrefixExpression(); @@ -454,7 +454,7 @@ public class KotlinExpressionParsing extends AbstractKotlinParsing { expression = mark(); } - parseCallExpression(); + parseSelectorCallExpression(); if (firstExpressionParsed) { expression.done(expressionType); @@ -515,7 +515,7 @@ public class KotlinExpressionParsing extends AbstractKotlinParsing { /* * atomicExpression typeParameters? valueParameters? functionLiteral* */ - private void parseCallExpression() { + private void parseSelectorCallExpression() { PsiBuilder.Marker mark = mark(); parseAtomicExpression(); if (!myBuilder.newlineBeforeCurrentToken() && parseCallSuffix()) { @@ -624,8 +624,6 @@ public class KotlinExpressionParsing extends AbstractKotlinParsing { * ; */ private boolean parseAtomicExpression() { - // System.out.println("atom at " + myBuilder.getTokenText()); - boolean ok = true; if (at(LPAR)) { @@ -670,9 +668,9 @@ public class KotlinExpressionParsing extends AbstractKotlinParsing { else if (at(DO_KEYWORD)) { parseDoWhile(); } - else if (atSet(CLASS_KEYWORD, INTERFACE_KEYWORD, FUN_KEYWORD, VAL_KEYWORD, - VAR_KEYWORD, TYPE_ALIAS_KEYWORD)) { - parseLocalDeclaration(); + else if (atSet(LOCAL_DECLARATION_FIRST) && + parseLocalDeclaration(/* rollbackIfDefinitelyNotExpression = */ myBuilder.newlineBeforeCurrentToken())) { + // declaration was parsed, do nothing } else if (at(IDENTIFIER)) { parseSimpleNameExpression(); @@ -1018,12 +1016,12 @@ public class KotlinExpressionParsing extends AbstractKotlinParsing { /* * modifiers declarationRest */ - private boolean parseLocalDeclaration() { + private boolean parseLocalDeclaration(boolean rollbackIfDefinitelyNotExpression) { PsiBuilder.Marker decl = mark(); KotlinParsing.ModifierDetector detector = new KotlinParsing.ModifierDetector(); myKotlinParsing.parseModifierList(detector, DEFAULT, TokenSet.EMPTY); - IElementType declType = parseLocalDeclarationRest(detector.isEnumDetected()); + IElementType declType = parseLocalDeclarationRest(detector.isEnumDetected(), rollbackIfDefinitelyNotExpression); if (declType != null) { // we do not attach preceding comments (non-doc) to local variables because they are likely commenting a few statements below @@ -1220,7 +1218,7 @@ public class KotlinExpressionParsing extends AbstractKotlinParsing { * ; */ private void parseStatement(boolean isScriptTopLevel) { - if (!parseLocalDeclaration()) { + if (!parseLocalDeclaration(/* rollbackIfDefinitelyNotExpression = */false)) { if (!atSet(EXPRESSION_FIRST)) { errorAndAdvance("Expecting a statement"); } @@ -1245,9 +1243,17 @@ public class KotlinExpressionParsing extends AbstractKotlinParsing { * : object * ; */ - private IElementType parseLocalDeclarationRest(boolean isEnum) { + @Nullable + private IElementType parseLocalDeclarationRest(boolean isEnum, boolean failIfDefinitelyNotExpression) { IElementType keywordToken = tt(); IElementType declType = null; + + if (failIfDefinitelyNotExpression) { + if (keywordToken != FUN_KEYWORD) return null; + + return myKotlinParsing.parseFunction(/* failIfIdentifierExists = */ true); + } + if (keywordToken == CLASS_KEYWORD || keywordToken == INTERFACE_KEYWORD) { declType = myKotlinParsing.parseClass(isEnum); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinParsing.java b/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinParsing.java index 715b688aa9b..9abbb3d7fdb 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinParsing.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinParsing.java @@ -21,6 +21,7 @@ import com.intellij.lang.WhitespacesBinders; import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.lexer.KtKeywordToken; @@ -1444,6 +1445,11 @@ public class KotlinParsing extends AbstractKotlinParsing { return accessorKind; } + @NotNull + IElementType parseFunction() { + return parseFunction(false); + } + /* * function * : modifiers "fun" typeParameters? @@ -1454,7 +1460,8 @@ public class KotlinParsing extends AbstractKotlinParsing { * functionBody? * ; */ - IElementType parseFunction() { + @Contract("false -> !null") + IElementType parseFunction(boolean failIfIdentifierExists) { assert _at(FUN_KEYWORD); advance(); // FUN_KEYWORD @@ -1476,6 +1483,11 @@ public class KotlinParsing extends AbstractKotlinParsing { TokenSet functionNameFollow = TokenSet.create(LT, LPAR, RPAR, COLON, EQ); boolean receiverFound = parseReceiverType("function", functionNameFollow); + if (at(IDENTIFIER) && failIfIdentifierExists) { + myBuilder.restoreJoiningComplexTokensState(); + return null; + } + // function as expression has no name parseFunctionOrPropertyName(receiverFound, "function", functionNameFollow, /*nameRequired = */ false); @@ -1598,15 +1610,15 @@ public class KotlinParsing extends AbstractKotlinParsing { /* * IDENTIFIER */ - private void parseFunctionOrPropertyName(boolean receiverFound, String title, TokenSet nameFollow, boolean nameRequired) { - if (!nameRequired && atSet(nameFollow)) return; // no name + private boolean parseFunctionOrPropertyName(boolean receiverFound, String title, TokenSet nameFollow, boolean nameRequired) { + if (!nameRequired && atSet(nameFollow)) return true; // no name TokenSet recoverySet = TokenSet.orSet(nameFollow, TokenSet.create(LBRACE, RBRACE), TOP_LEVEL_DECLARATION_FIRST); if (!receiverFound) { - expect(IDENTIFIER, "Expecting " + title + " name or receiver type", recoverySet); + return expect(IDENTIFIER, "Expecting " + title + " name or receiver type", recoverySet); } else { - expect(IDENTIFIER, "Expecting " + title + " name", recoverySet); + return expect(IDENTIFIER, "Expecting " + title + " name", recoverySet); } } diff --git a/compiler/testData/diagnostics/tests/declarationChecks/namedFunAsLastExpressionInBlock.kt b/compiler/testData/diagnostics/tests/declarationChecks/namedFunAsLastExpressionInBlock.kt index e72fe43bc1f..2ff0e612f2c 100644 --- a/compiler/testData/diagnostics/tests/declarationChecks/namedFunAsLastExpressionInBlock.kt +++ b/compiler/testData/diagnostics/tests/declarationChecks/namedFunAsLastExpressionInBlock.kt @@ -12,18 +12,18 @@ fun test() { val x1 = if (1 == 1) // TODO: Diagnostic content could be better - Int)!>fun named4(): Int {return 1} - else - fun named5() = 1 + fun named4(): Int {return 1} + else + fun named5() = 1 val x2 = if (1 == 1) { - fun named6(): Int { + fun named6(): Int { return 1 - } + } } else - fun named7() = 1 + fun named7() = 1 val x3 = when (1) { 0 -> fun named8(): Int {return 1} diff --git a/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/declarationAfterDotSelectorExpected.kt b/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/declarationAfterDotSelectorExpected.kt new file mode 100644 index 00000000000..3d8902c35ac --- /dev/null +++ b/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/declarationAfterDotSelectorExpected.kt @@ -0,0 +1,29 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE + +fun foo(x: Any) { + x. + val foo = 1 + + x. + fun bar() = 2 + + x. + fun String.() = 3 + + var a = 24. + var b = 42.0 +} + +class A { + val z = "a". + val x = 4 + + val y = "b". + fun baz() = 5 + + val q = "c". + fun String.() = 6 + + var a = 24. + var b = 42.0 +} diff --git a/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/declarationAfterDotSelectorExpected.txt b/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/declarationAfterDotSelectorExpected.txt new file mode 100644 index 00000000000..d07be9fe3dd --- /dev/null +++ b/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/declarationAfterDotSelectorExpected.txt @@ -0,0 +1,18 @@ +package + +public fun foo(/*0*/ x: kotlin.Any): kotlin.Unit + +public final class A { + public constructor A() + public final var a: [ERROR : Type for 24.] + public final var b: kotlin.Double + public final val q: [ERROR : Type for "c". + fun String.() = 6] + public final val x: kotlin.Int = 4 + public final val y: [ERROR : Type for "b".] + public final val z: [ERROR : Type for "a".] + public final fun baz(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/declarationAfterIncompleteElvis.kt b/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/declarationAfterIncompleteElvis.kt new file mode 100644 index 00000000000..1ddc5f31308 --- /dev/null +++ b/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/declarationAfterIncompleteElvis.kt @@ -0,0 +1,22 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE +fun foo(x: Any?) { + x ?: + val foo = 1 + + x ?: + fun bar() = 2 + + val res: String.() -> Int = null ?: + fun String.() = 3 +} + +class A { + val z = null ?: + val x = 4 + + val y = null ?: + fun baz() = 5 + + val q = null ?: + fun String.() = 6 +} diff --git a/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/declarationAfterIncompleteElvis.txt b/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/declarationAfterIncompleteElvis.txt new file mode 100644 index 00000000000..c193d4d17a5 --- /dev/null +++ b/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/declarationAfterIncompleteElvis.txt @@ -0,0 +1,15 @@ +package + +public fun foo(/*0*/ x: kotlin.Any?): kotlin.Unit + +public final class A { + public constructor A() + public final val q: kotlin.String.() -> kotlin.Int + public final val x: kotlin.Int = 4 + public final val y: [ERROR : Type for null ?:] + public final val z: [ERROR : Type for null ?:] + public final fun baz(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/incompleteCode/variableDeclarationInSelector.kt b/compiler/testData/diagnostics/tests/incompleteCode/variableDeclarationInSelector.kt index 1b3b0686a26..22dedae5092 100644 --- a/compiler/testData/diagnostics/tests/incompleteCode/variableDeclarationInSelector.kt +++ b/compiler/testData/diagnostics/tests/incompleteCode/variableDeclarationInSelector.kt @@ -1,4 +1,4 @@ fun foo(s: String) { - s. - val b = 42 -} \ No newline at end of file + s. + val b = 42 +} diff --git a/compiler/testData/psi/recovery/DeclarationAfterDotSelectorExpected.kt b/compiler/testData/psi/recovery/DeclarationAfterDotSelectorExpected.kt new file mode 100644 index 00000000000..a154d8e24a9 --- /dev/null +++ b/compiler/testData/psi/recovery/DeclarationAfterDotSelectorExpected.kt @@ -0,0 +1,27 @@ +fun foo(x: Any) { + x. + val foo = 1 + + x. + fun bar() = 2 + + x. + fun String.() = 3 + + var a = 24. + var b = 42.0 +} + +class A { + val z = "a". + val x = 4 + + val y = "b". + fun baz() = 5 + + val q = "c". + fun String.() = 6 + + var a = 24. + var b = 42.0 +} diff --git a/compiler/testData/psi/recovery/DeclarationAfterDotSelectorExpected.txt b/compiler/testData/psi/recovery/DeclarationAfterDotSelectorExpected.txt new file mode 100644 index 00000000000..626cd998128 --- /dev/null +++ b/compiler/testData/psi/recovery/DeclarationAfterDotSelectorExpected.txt @@ -0,0 +1,231 @@ +JetFile: DeclarationAfterDotSelectorExpected.kt + PACKAGE_DIRECTIVE + + IMPORT_LIST + + FUN + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('foo') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + VALUE_PARAMETER + PsiElement(IDENTIFIER)('x') + PsiElement(COLON)(':') + PsiWhiteSpace(' ') + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('Any') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + BLOCK + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + DOT_QUALIFIED_EXPRESSION + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('x') + PsiElement(DOT)('.') + PsiErrorElement:Expecting an element + + PsiWhiteSpace('\n ') + PROPERTY + PsiElement(val)('val') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('foo') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('1') + PsiWhiteSpace('\n\n ') + DOT_QUALIFIED_EXPRESSION + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('x') + PsiElement(DOT)('.') + PsiErrorElement:Expecting an element + + PsiWhiteSpace('\n ') + FUN + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('bar') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('2') + PsiWhiteSpace('\n\n ') + DOT_QUALIFIED_EXPRESSION + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('x') + PsiElement(DOT)('.') + PsiWhiteSpace('\n ') + FUN + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('String') + PsiElement(DOT)('.') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('3') + PsiWhiteSpace('\n\n ') + PROPERTY + PsiElement(var)('var') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('a') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + DOT_QUALIFIED_EXPRESSION + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('24') + PsiElement(DOT)('.') + PsiErrorElement:Expecting an element + + PsiWhiteSpace('\n ') + PROPERTY + PsiElement(var)('var') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('b') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + FLOAT_CONSTANT + PsiElement(FLOAT_CONSTANT)('42.0') + PsiWhiteSpace('\n') + PsiElement(RBRACE)('}') + PsiWhiteSpace('\n\n') + CLASS + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('A') + PsiWhiteSpace(' ') + CLASS_BODY + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + PROPERTY + PsiElement(val)('val') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('z') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + DOT_QUALIFIED_EXPRESSION + STRING_TEMPLATE + PsiElement(OPEN_QUOTE)('"') + LITERAL_STRING_TEMPLATE_ENTRY + PsiElement(REGULAR_STRING_PART)('a') + PsiElement(CLOSING_QUOTE)('"') + PsiElement(DOT)('.') + PsiErrorElement:Expecting an element + + PsiWhiteSpace('\n ') + PROPERTY + PsiElement(val)('val') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('x') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('4') + PsiWhiteSpace('\n\n ') + PROPERTY + PsiElement(val)('val') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('y') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + DOT_QUALIFIED_EXPRESSION + STRING_TEMPLATE + PsiElement(OPEN_QUOTE)('"') + LITERAL_STRING_TEMPLATE_ENTRY + PsiElement(REGULAR_STRING_PART)('b') + PsiElement(CLOSING_QUOTE)('"') + PsiElement(DOT)('.') + PsiErrorElement:Expecting an element + + PsiWhiteSpace('\n ') + FUN + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('baz') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('5') + PsiWhiteSpace('\n\n ') + PROPERTY + PsiElement(val)('val') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('q') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + DOT_QUALIFIED_EXPRESSION + STRING_TEMPLATE + PsiElement(OPEN_QUOTE)('"') + LITERAL_STRING_TEMPLATE_ENTRY + PsiElement(REGULAR_STRING_PART)('c') + PsiElement(CLOSING_QUOTE)('"') + PsiElement(DOT)('.') + PsiWhiteSpace('\n ') + FUN + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('String') + PsiElement(DOT)('.') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('6') + PsiWhiteSpace('\n\n ') + PROPERTY + PsiElement(var)('var') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('a') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + DOT_QUALIFIED_EXPRESSION + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('24') + PsiElement(DOT)('.') + PsiErrorElement:Expecting an element + + PsiWhiteSpace('\n ') + PROPERTY + PsiElement(var)('var') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('b') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + FLOAT_CONSTANT + PsiElement(FLOAT_CONSTANT)('42.0') + PsiWhiteSpace('\n') + PsiElement(RBRACE)('}') diff --git a/compiler/testData/psi/recovery/DeclarationAfterIncompleteElvis.kt b/compiler/testData/psi/recovery/DeclarationAfterIncompleteElvis.kt new file mode 100644 index 00000000000..4e36338c19a --- /dev/null +++ b/compiler/testData/psi/recovery/DeclarationAfterIncompleteElvis.kt @@ -0,0 +1,21 @@ +fun foo(x: Any?) { + x ?: + val foo = 1 + + x ?: + fun bar() = 2 + + x ?: + fun String.() = 3 +} + +class A { + val z = null ?: + val x = 4 + + val y = null ?: + fun baz() = 5 + + val q = null ?: + fun String.() = 6 +} diff --git a/compiler/testData/psi/recovery/DeclarationAfterIncompleteElvis.txt b/compiler/testData/psi/recovery/DeclarationAfterIncompleteElvis.txt new file mode 100644 index 00000000000..b6171471643 --- /dev/null +++ b/compiler/testData/psi/recovery/DeclarationAfterIncompleteElvis.txt @@ -0,0 +1,188 @@ +JetFile: DeclarationAfterIncompleteElvis.kt + PACKAGE_DIRECTIVE + + IMPORT_LIST + + FUN + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('foo') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + VALUE_PARAMETER + PsiElement(IDENTIFIER)('x') + PsiElement(COLON)(':') + PsiWhiteSpace(' ') + TYPE_REFERENCE + NULLABLE_TYPE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('Any') + PsiElement(QUEST)('?') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + BLOCK + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + BINARY_EXPRESSION + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('x') + PsiWhiteSpace(' ') + OPERATION_REFERENCE + PsiElement(ELVIS)('?:') + PsiErrorElement:Expecting an element + + PsiWhiteSpace('\n ') + PROPERTY + PsiElement(val)('val') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('foo') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('1') + PsiWhiteSpace('\n\n ') + BINARY_EXPRESSION + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('x') + PsiWhiteSpace(' ') + OPERATION_REFERENCE + PsiElement(ELVIS)('?:') + PsiErrorElement:Expecting an element + + PsiWhiteSpace('\n ') + FUN + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('bar') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('2') + PsiWhiteSpace('\n\n ') + BINARY_EXPRESSION + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('x') + PsiWhiteSpace(' ') + OPERATION_REFERENCE + PsiElement(ELVIS)('?:') + PsiWhiteSpace('\n ') + FUN + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('String') + PsiElement(DOT)('.') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('3') + PsiWhiteSpace('\n') + PsiElement(RBRACE)('}') + PsiWhiteSpace('\n\n') + CLASS + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('A') + PsiWhiteSpace(' ') + CLASS_BODY + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + PROPERTY + PsiElement(val)('val') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('z') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + BINARY_EXPRESSION + NULL + PsiElement(null)('null') + PsiWhiteSpace(' ') + OPERATION_REFERENCE + PsiElement(ELVIS)('?:') + PsiErrorElement:Expecting an element + + PsiWhiteSpace('\n ') + PROPERTY + PsiElement(val)('val') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('x') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('4') + PsiWhiteSpace('\n\n ') + PROPERTY + PsiElement(val)('val') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('y') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + BINARY_EXPRESSION + NULL + PsiElement(null)('null') + PsiWhiteSpace(' ') + OPERATION_REFERENCE + PsiElement(ELVIS)('?:') + PsiErrorElement:Expecting an element + + PsiWhiteSpace('\n ') + FUN + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('baz') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('5') + PsiWhiteSpace('\n\n ') + PROPERTY + PsiElement(val)('val') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('q') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + BINARY_EXPRESSION + NULL + PsiElement(null)('null') + PsiWhiteSpace(' ') + OPERATION_REFERENCE + PsiElement(ELVIS)('?:') + PsiWhiteSpace('\n ') + FUN + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('String') + PsiElement(DOT)('.') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('6') + PsiWhiteSpace('\n') + PsiElement(RBRACE)('}') diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 0326caa2846..5f10cfe557f 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -8723,6 +8723,18 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("declarationAfterDotSelectorExpected.kt") + public void testDeclarationAfterDotSelectorExpected() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/declarationAfterDotSelectorExpected.kt"); + doTest(fileName); + } + + @TestMetadata("declarationAfterIncompleteElvis.kt") + public void testDeclarationAfterIncompleteElvis() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/declarationAfterIncompleteElvis.kt"); + doTest(fileName); + } + @TestMetadata("funEquals.kt") public void testFunEquals() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/funEquals.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/parsing/ParsingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/parsing/ParsingTestGenerated.java index ec8943bd17d..bc03f38f286 100644 --- a/compiler/tests/org/jetbrains/kotlin/parsing/ParsingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/parsing/ParsingTestGenerated.java @@ -1992,6 +1992,18 @@ public class ParsingTestGenerated extends AbstractParsingTest { doParsingTest(fileName); } + @TestMetadata("DeclarationAfterDotSelectorExpected.kt") + public void testDeclarationAfterDotSelectorExpected() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/psi/recovery/DeclarationAfterDotSelectorExpected.kt"); + doParsingTest(fileName); + } + + @TestMetadata("DeclarationAfterIncompleteElvis.kt") + public void testDeclarationAfterIncompleteElvis() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/psi/recovery/DeclarationAfterIncompleteElvis.kt"); + doParsingTest(fileName); + } + @TestMetadata("DoWhileWithEmptyCondition.kt") public void testDoWhileWithEmptyCondition() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/psi/recovery/DoWhileWithEmptyCondition.kt");