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:
Denis Zharkov
2016-07-07 16:26:30 +03:00
parent 141db1809f
commit f1026935a0
12 changed files with 457 additions and 3 deletions
@@ -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
@@ -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() {
}
}
@@ -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
}
@@ -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<!>
}
@@ -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
@@ -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() {
}
}
@@ -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)('}')
+41
View File
@@ -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
}
+191
View File
@@ -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");