Add recovery for properties without name
If property name is parsed on the next line and declaration is invalid (no receiver/type/initializer), treat that name as it does not belong to property #KT-12987 In Progress
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
abstract class A {
|
||||
private val<!SYNTAX!><!>
|
||||
// private is parsed as val's identifier
|
||||
private fun foo1() {
|
||||
}
|
||||
|
||||
private val<!SYNTAX!><!>
|
||||
protected abstract fun foo2()
|
||||
|
||||
private val<!SYNTAX!><!>
|
||||
fun foo3() {
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package
|
||||
|
||||
public abstract class A {
|
||||
public constructor A()
|
||||
private final val <no name provided>: [ERROR : No type, no body]
|
||||
private final val <no name provided>: [ERROR : No type, no body]
|
||||
private final val <no name provided>: [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
|
||||
}
|
||||
compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/valWithNoNameInBlock.kt
Vendored
+50
@@ -0,0 +1,50 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE
|
||||
|
||||
fun println(x: String) {
|
||||
}
|
||||
|
||||
fun run(block: () -> Unit) {}
|
||||
|
||||
val propertyNameOnTheNextLine = 1
|
||||
|
||||
fun foo() {
|
||||
val<!SYNTAX!><!>
|
||||
println("abc")
|
||||
|
||||
val<!SYNTAX!><!>
|
||||
run {
|
||||
println("abc")
|
||||
}
|
||||
|
||||
val<!SYNTAX!><!>
|
||||
if (1 == 1) {
|
||||
|
||||
}
|
||||
|
||||
val<!SYNTAX!><!>
|
||||
(1 + 2)
|
||||
|
||||
// `propertyNameOnTheNextLine` parsed as simple name expression
|
||||
val<!SYNTAX!><!>
|
||||
propertyNameOnTheNextLine
|
||||
|
||||
val<!SYNTAX!><!>
|
||||
// comment
|
||||
propertyNameOnTheNextLine
|
||||
|
||||
val<!SYNTAX!><!> /* comment */
|
||||
propertyNameOnTheNextLine
|
||||
|
||||
// Correct properties
|
||||
val
|
||||
property1 = 1
|
||||
|
||||
val
|
||||
propertyWithBy by <!UNRESOLVED_REFERENCE!>lazy<!> { 1 }
|
||||
|
||||
val
|
||||
propertyWithType: Int
|
||||
|
||||
val
|
||||
(a, b) = <!COMPONENT_FUNCTION_MISSING, COMPONENT_FUNCTION_MISSING!>1<!>
|
||||
}
|
||||
+6
@@ -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
|
||||
+13
@@ -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() {
|
||||
}
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
JetFile: ValWithNoNameBeforeNextDeclarationWithModifiers.kt
|
||||
PACKAGE_DIRECTIVE
|
||||
<empty list>
|
||||
IMPORT_LIST
|
||||
<empty 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
|
||||
<empty list>
|
||||
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
|
||||
<empty list>
|
||||
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
|
||||
<empty list>
|
||||
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)('}')
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
JetFile: ValWithNoNameInBlock.kt
|
||||
PACKAGE_DIRECTIVE
|
||||
<empty list>
|
||||
IMPORT_LIST
|
||||
<empty 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
|
||||
<empty list>
|
||||
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
|
||||
<empty list>
|
||||
PsiWhiteSpace('\n ')
|
||||
CALL_EXPRESSION
|
||||
REFERENCE_EXPRESSION
|
||||
PsiElement(IDENTIFIER)('lambdaCall')
|
||||
PsiWhiteSpace(' ')
|
||||
LAMBDA_ARGUMENT
|
||||
LAMBDA_EXPRESSION
|
||||
FUNCTION_LITERAL
|
||||
PsiElement(LBRACE)('{')
|
||||
PsiWhiteSpace('\n\n ')
|
||||
BLOCK
|
||||
<empty list>
|
||||
PsiElement(RBRACE)('}')
|
||||
PsiWhiteSpace('\n\n ')
|
||||
PROPERTY
|
||||
PsiElement(val)('val')
|
||||
PsiErrorElement:Expecting property name or receiver type
|
||||
<empty list>
|
||||
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
|
||||
<empty list>
|
||||
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
|
||||
<empty list>
|
||||
PsiWhiteSpace('\n ')
|
||||
REFERENCE_EXPRESSION
|
||||
PsiElement(IDENTIFIER)('propertyNameOnTheNextLine')
|
||||
PsiWhiteSpace('\n\n ')
|
||||
PROPERTY
|
||||
PsiElement(val)('val')
|
||||
PsiErrorElement:Expecting property name or receiver type
|
||||
<empty list>
|
||||
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
|
||||
<empty list>
|
||||
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)('}')
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user