diff --git a/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinParsing.java b/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinParsing.java index 375f087d9ca..22b92e98bf7 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinParsing.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinParsing.java @@ -1232,6 +1232,9 @@ public class KotlinParsing extends AbstractKotlinParsing { errorIf(receiver, multiDeclaration && receiverTypeDeclared, "Receiver type is not allowed on a destructuring declaration"); + boolean isNameOnTheNextLine = eol(); + PsiBuilder.Marker beforeName = mark(); + if (multiDeclaration) { PsiBuilder.Marker multiDecl = mark(); parseMultiDeclarationName(propertyNameFollow); @@ -1243,7 +1246,9 @@ public class KotlinParsing extends AbstractKotlinParsing { myBuilder.restoreJoiningComplexTokensState(); + boolean noTypeReference = true; if (at(COLON)) { + noTypeReference = false; PsiBuilder.Marker type = mark(); advance(); // COLON parseTypeRef(); @@ -1252,7 +1257,16 @@ public class KotlinParsing extends AbstractKotlinParsing { parseTypeConstraintsGuarded(typeParametersDeclared); - parsePropertyDelegateOrAssignment(); + if (!parsePropertyDelegateOrAssignment() && isNameOnTheNextLine && noTypeReference && !receiverTypeDeclared) { + // Do not parse property identifier on the next line if declaration is invalid + // In most cases this identifier relates to next statement/declaration + beforeName.rollbackTo(); + error("Expecting property name or receiver type"); + return PROPERTY; + } + + beforeName.drop(); + if (!local) { // It's only needed for non-local properties, because in local ones: // "val a = 1; b" must not be an infix call of b on "val ...;" @@ -1276,14 +1290,18 @@ public class KotlinParsing extends AbstractKotlinParsing { return multiDeclaration ? DESTRUCTURING_DECLARATION : PROPERTY; } - private void parsePropertyDelegateOrAssignment() { + private boolean parsePropertyDelegateOrAssignment() { if (at(BY_KEYWORD)) { parsePropertyDelegate(); + return true; } else if (at(EQ)) { advance(); // EQ myExpressionParsing.parseExpression(); + return true; } + + return false; } /* diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt index 845683e34e6..633baa92014 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt @@ -138,7 +138,7 @@ class KtPsiFactory(private val project: Project) { } fun createWhiteSpace(text: String): PsiElement { - return createProperty("val${text}x").findElementAt(3)!! + return createProperty("val${text}x: Int").findElementAt(3)!! } // Remove when all Java usages are rewritten to Kotlin diff --git a/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/valWithNoNameBeforeNextDeclarationWithModifiers.kt b/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/valWithNoNameBeforeNextDeclarationWithModifiers.kt new file mode 100644 index 00000000000..d622c23d66c --- /dev/null +++ b/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/valWithNoNameBeforeNextDeclarationWithModifiers.kt @@ -0,0 +1,13 @@ +abstract class A { + private val + // private is parsed as val's identifier + private fun foo1() { + } + + private val + protected abstract fun foo2() + + private val + fun foo3() { + } +} diff --git a/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/valWithNoNameBeforeNextDeclarationWithModifiers.txt b/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/valWithNoNameBeforeNextDeclarationWithModifiers.txt new file mode 100644 index 00000000000..5529fe20409 --- /dev/null +++ b/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/valWithNoNameBeforeNextDeclarationWithModifiers.txt @@ -0,0 +1,14 @@ +package + +public abstract class A { + public constructor A() + private final val : [ERROR : No type, no body] + private final val : [ERROR : No type, no body] + private final val : [ERROR : No type, no body] + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + private final fun foo1(): kotlin.Unit + protected abstract fun foo2(): kotlin.Unit + public final fun foo3(): kotlin.Unit + 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/valWithNoNameInBlock.kt b/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/valWithNoNameInBlock.kt new file mode 100644 index 00000000000..888589958ef --- /dev/null +++ b/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/valWithNoNameInBlock.kt @@ -0,0 +1,50 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE + +fun println(x: String) { +} + +fun run(block: () -> Unit) {} + +val propertyNameOnTheNextLine = 1 + +fun foo() { + val + println("abc") + + val + run { + println("abc") + } + + val + if (1 == 1) { + + } + + val + (1 + 2) + + // `propertyNameOnTheNextLine` parsed as simple name expression + val + propertyNameOnTheNextLine + + val + // comment + propertyNameOnTheNextLine + + val /* comment */ + propertyNameOnTheNextLine + + // Correct properties + val + property1 = 1 + + val + propertyWithBy by lazy { 1 } + + val + propertyWithType: Int + + val + (a, b) = 1 +} diff --git a/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/valWithNoNameInBlock.txt b/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/valWithNoNameInBlock.txt new file mode 100644 index 00000000000..a5a6440d36e --- /dev/null +++ b/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/valWithNoNameInBlock.txt @@ -0,0 +1,6 @@ +package + +public val propertyNameOnTheNextLine: kotlin.Int = 1 +public fun foo(): kotlin.Unit +public fun println(/*0*/ x: kotlin.String): kotlin.Unit +public fun run(/*0*/ block: () -> kotlin.Unit): kotlin.Unit diff --git a/compiler/testData/psi/recovery/ValWithNoNameBeforeNextDeclarationWithModifiers.kt b/compiler/testData/psi/recovery/ValWithNoNameBeforeNextDeclarationWithModifiers.kt new file mode 100644 index 00000000000..ba305e0cdb2 --- /dev/null +++ b/compiler/testData/psi/recovery/ValWithNoNameBeforeNextDeclarationWithModifiers.kt @@ -0,0 +1,13 @@ +class A { + private val + // private is parsed as val's identifier + private fun foo1() { + } + + private val + private abstract inline fun foo2() + + private val + fun foo3() { + } +} diff --git a/compiler/testData/psi/recovery/ValWithNoNameBeforeNextDeclarationWithModifiers.txt b/compiler/testData/psi/recovery/ValWithNoNameBeforeNextDeclarationWithModifiers.txt new file mode 100644 index 00000000000..231a1122cf7 --- /dev/null +++ b/compiler/testData/psi/recovery/ValWithNoNameBeforeNextDeclarationWithModifiers.txt @@ -0,0 +1,84 @@ +JetFile: ValWithNoNameBeforeNextDeclarationWithModifiers.kt + PACKAGE_DIRECTIVE + + IMPORT_LIST + + CLASS + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('A') + PsiWhiteSpace(' ') + CLASS_BODY + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + PROPERTY + MODIFIER_LIST + PsiElement(private)('private') + PsiWhiteSpace(' ') + PsiElement(val)('val') + PsiErrorElement:Expecting property name or receiver type + + PsiWhiteSpace('\n ') + FUN + PsiComment(EOL_COMMENT)('// private is parsed as val's identifier') + PsiWhiteSpace('\n ') + MODIFIER_LIST + PsiElement(private)('private') + PsiWhiteSpace(' ') + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('foo1') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + BLOCK + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + PsiElement(RBRACE)('}') + PsiWhiteSpace('\n\n ') + PROPERTY + MODIFIER_LIST + PsiElement(private)('private') + PsiWhiteSpace(' ') + PsiElement(val)('val') + PsiErrorElement:Expecting property name or receiver type + + PsiWhiteSpace('\n ') + FUN + MODIFIER_LIST + PsiElement(private)('private') + PsiWhiteSpace(' ') + PsiElement(abstract)('abstract') + PsiWhiteSpace(' ') + PsiElement(inline)('inline') + PsiWhiteSpace(' ') + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('foo2') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n\n ') + PROPERTY + MODIFIER_LIST + PsiElement(private)('private') + PsiWhiteSpace(' ') + PsiElement(val)('val') + PsiErrorElement:Expecting property name or receiver type + + PsiWhiteSpace('\n ') + FUN + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('foo3') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + BLOCK + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + PsiElement(RBRACE)('}') + PsiWhiteSpace('\n') + PsiElement(RBRACE)('}') diff --git a/compiler/testData/psi/recovery/ValWithNoNameInBlock.kt b/compiler/testData/psi/recovery/ValWithNoNameInBlock.kt new file mode 100644 index 00000000000..22a42820404 --- /dev/null +++ b/compiler/testData/psi/recovery/ValWithNoNameInBlock.kt @@ -0,0 +1,41 @@ +fun foo() { + val + println("abc") + + val + lambdaCall { + + } + + val + if (1 == 1) { + + } + + val + (1 + 2) + + // `propertyNameOnTheNextLine` parsed as simple name expression + val + propertyNameOnTheNextLine + + val + // comment + propertyNameOnTheNextLine + + val /* comment */ + propertyNameOnTheNextLine + + // Correct properties + val + property1 = 1 + + val + propertyWithBy by lazy { 1 } + + val + propertyWithType: Int + + val + (a, b) = 1 +} diff --git a/compiler/testData/psi/recovery/ValWithNoNameInBlock.txt b/compiler/testData/psi/recovery/ValWithNoNameInBlock.txt new file mode 100644 index 00000000000..1f2e3d477a2 --- /dev/null +++ b/compiler/testData/psi/recovery/ValWithNoNameInBlock.txt @@ -0,0 +1,191 @@ +JetFile: ValWithNoNameInBlock.kt + PACKAGE_DIRECTIVE + + IMPORT_LIST + + FUN + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('foo') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + BLOCK + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + PROPERTY + PsiElement(val)('val') + PsiErrorElement:Expecting property name or receiver type + + PsiWhiteSpace('\n ') + CALL_EXPRESSION + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('println') + VALUE_ARGUMENT_LIST + PsiElement(LPAR)('(') + VALUE_ARGUMENT + STRING_TEMPLATE + PsiElement(OPEN_QUOTE)('"') + LITERAL_STRING_TEMPLATE_ENTRY + PsiElement(REGULAR_STRING_PART)('abc') + PsiElement(CLOSING_QUOTE)('"') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n\n ') + PROPERTY + PsiElement(val)('val') + PsiErrorElement:Expecting property name or receiver type + + PsiWhiteSpace('\n ') + CALL_EXPRESSION + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('lambdaCall') + PsiWhiteSpace(' ') + LAMBDA_ARGUMENT + LAMBDA_EXPRESSION + FUNCTION_LITERAL + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n\n ') + BLOCK + + PsiElement(RBRACE)('}') + PsiWhiteSpace('\n\n ') + PROPERTY + PsiElement(val)('val') + PsiErrorElement:Expecting property name or receiver type + + PsiWhiteSpace('\n ') + IF + PsiElement(if)('if') + PsiWhiteSpace(' ') + PsiElement(LPAR)('(') + CONDITION + BINARY_EXPRESSION + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('1') + PsiWhiteSpace(' ') + OPERATION_REFERENCE + PsiElement(EQEQ)('==') + PsiWhiteSpace(' ') + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('1') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + THEN + BLOCK + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n\n ') + PsiElement(RBRACE)('}') + PsiWhiteSpace('\n\n ') + PROPERTY + PsiElement(val)('val') + PsiErrorElement:Expecting property name or receiver type + + PsiWhiteSpace('\n ') + PARENTHESIZED + PsiElement(LPAR)('(') + BINARY_EXPRESSION + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('1') + PsiWhiteSpace(' ') + OPERATION_REFERENCE + PsiElement(PLUS)('+') + PsiWhiteSpace(' ') + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('2') + PsiElement(RPAR)(')') + PsiWhiteSpace('\n\n ') + PsiComment(EOL_COMMENT)('// `propertyNameOnTheNextLine` parsed as simple name expression') + PsiWhiteSpace('\n ') + PROPERTY + PsiElement(val)('val') + PsiErrorElement:Expecting property name or receiver type + + PsiWhiteSpace('\n ') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('propertyNameOnTheNextLine') + PsiWhiteSpace('\n\n ') + PROPERTY + PsiElement(val)('val') + PsiErrorElement:Expecting property name or receiver type + + PsiWhiteSpace('\n ') + PsiComment(EOL_COMMENT)('// comment') + PsiWhiteSpace('\n ') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('propertyNameOnTheNextLine') + PsiWhiteSpace('\n\n ') + PROPERTY + PsiElement(val)('val') + PsiErrorElement:Expecting property name or receiver type + + PsiWhiteSpace(' ') + PsiComment(BLOCK_COMMENT)('/* comment */') + PsiWhiteSpace('\n ') + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('propertyNameOnTheNextLine') + PsiWhiteSpace('\n\n ') + PsiComment(EOL_COMMENT)('// Correct properties') + PsiWhiteSpace('\n ') + PROPERTY + PsiElement(val)('val') + PsiWhiteSpace('\n ') + PsiElement(IDENTIFIER)('property1') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('1') + PsiWhiteSpace('\n\n ') + PROPERTY + PsiElement(val)('val') + PsiWhiteSpace('\n ') + PsiElement(IDENTIFIER)('propertyWithBy') + PsiWhiteSpace(' ') + PROPERTY_DELEGATE + PsiElement(by)('by') + PsiWhiteSpace(' ') + CALL_EXPRESSION + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('lazy') + PsiWhiteSpace(' ') + LAMBDA_ARGUMENT + LAMBDA_EXPRESSION + FUNCTION_LITERAL + PsiElement(LBRACE)('{') + PsiWhiteSpace(' ') + BLOCK + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('1') + PsiWhiteSpace(' ') + PsiElement(RBRACE)('}') + PsiWhiteSpace('\n\n ') + PROPERTY + PsiElement(val)('val') + PsiWhiteSpace('\n ') + PsiElement(IDENTIFIER)('propertyWithType') + PsiElement(COLON)(':') + PsiWhiteSpace(' ') + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('Int') + PsiWhiteSpace('\n\n ') + DESTRUCTURING_DECLARATION + PsiElement(val)('val') + PsiWhiteSpace('\n ') + PsiElement(LPAR)('(') + DESTRUCTURING_DECLARATION_ENTRY + PsiElement(IDENTIFIER)('a') + PsiElement(COMMA)(',') + PsiWhiteSpace(' ') + DESTRUCTURING_DECLARATION_ENTRY + PsiElement(IDENTIFIER)('b') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('1') + 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 bfc5c45987b..7b28522df78 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -8782,6 +8782,18 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/valNoName.kt"); doTest(fileName); } + + @TestMetadata("valWithNoNameBeforeNextDeclarationWithModifiers.kt") + public void testValWithNoNameBeforeNextDeclarationWithModifiers() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/valWithNoNameBeforeNextDeclarationWithModifiers.kt"); + doTest(fileName); + } + + @TestMetadata("valWithNoNameInBlock.kt") + public void testValWithNoNameInBlock() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/valWithNoNameInBlock.kt"); + doTest(fileName); + } } } diff --git a/compiler/tests/org/jetbrains/kotlin/parsing/ParsingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/parsing/ParsingTestGenerated.java index 133646a63c0..ec8943bd17d 100644 --- a/compiler/tests/org/jetbrains/kotlin/parsing/ParsingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/parsing/ParsingTestGenerated.java @@ -2268,6 +2268,18 @@ public class ParsingTestGenerated extends AbstractParsingTest { doParsingTest(fileName); } + @TestMetadata("ValWithNoNameBeforeNextDeclarationWithModifiers.kt") + public void testValWithNoNameBeforeNextDeclarationWithModifiers() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/psi/recovery/ValWithNoNameBeforeNextDeclarationWithModifiers.kt"); + doParsingTest(fileName); + } + + @TestMetadata("ValWithNoNameInBlock.kt") + public void testValWithNoNameInBlock() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/psi/recovery/ValWithNoNameInBlock.kt"); + doParsingTest(fileName); + } + @TestMetadata("ValueParameterNoTypeRecovery.kt") public void testValueParameterNoTypeRecovery() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/psi/recovery/ValueParameterNoTypeRecovery.kt");