Add recovery for import lists in the middle of the file

Note, that it only works for top-level declarations

^KT-7185 Fixed
This commit is contained in:
Denis Zharkov
2018-12-21 13:53:10 +03:00
parent e6710b6fa2
commit 65e6e21d0f
10 changed files with 371 additions and 8 deletions
@@ -424,7 +424,12 @@ public class KotlinParsing extends AbstractKotlinParsing {
declType = FUN;
}
if (declType == null) {
if (declType == null && at(IMPORT_KEYWORD)) {
error("imports are only allowed in the beginning of file");
parseImportDirectives();
decl.drop();
}
else if (declType == null) {
errorAndAdvance("Expecting a top level declaration");
decl.drop();
}
@@ -22,6 +22,7 @@ import com.intellij.openapi.fileTypes.FileType
import com.intellij.psi.*
import com.intellij.psi.stubs.StubElement
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.ArrayFactory
import com.intellij.util.FileContentUtilCore
import com.intellij.util.IncorrectOperationException
import org.jetbrains.kotlin.KtNodeTypes
@@ -54,13 +55,16 @@ open class KtFile(viewProvider: FileViewProvider, val isCompiled: Boolean) :
private var pathCached: String? = null
val importList: KtImportList?
get() = findChildByTypeOrClass(KtStubElementTypes.IMPORT_LIST, KtImportList::class.java)
get() = importLists.firstOrNull()
private val importLists: Array<out KtImportList>
get() = findChildrenByTypeOrClass(KtStubElementTypes.IMPORT_LIST, KtImportList::class.java)
val fileAnnotationList: KtFileAnnotationList?
get() = findChildByTypeOrClass(KtStubElementTypes.FILE_ANNOTATION_LIST, KtFileAnnotationList::class.java)
open val importDirectives: List<KtImportDirective>
get() = importList?.imports ?: emptyList()
get() = importLists.flatMap { it.imports }
// scripts have no package directive, all other files must have package directives
val packageDirective: KtPackageDirective?
@@ -154,6 +158,19 @@ open class KtFile(viewProvider: FileViewProvider, val isCompiled: Boolean) :
return findChildByClass(elementClass)
}
fun <T : KtElementImplStub<out StubElement<*>>> findChildrenByTypeOrClass(
elementType: KtPlaceHolderStubElementType<T>,
elementClass: Class<T>
): Array<out T> {
val stub = stub
if (stub != null) {
val arrayFactory: ArrayFactory<T> = elementType.arrayFactory
return stub.getChildrenByType(elementType, arrayFactory)
}
return findChildrenByClass(elementClass)
}
fun findImportByAlias(name: String): KtImportDirective? =
importDirectives.firstOrNull { name == it.aliasName }
@@ -0,0 +1,105 @@
// !WITH_NEW_INFERENCE
// FILE:a.kt
package a
<!OI;SYNTAX!><<!><!OI;SYNTAX!><<!><!OI;SYNTAX!><<!> <!OI;SYNTAX!>FOOO<!><!OI;SYNTAX!><!>
import b.B //class
import b.foo //function
import b.ext //extension function
import b.value //property
import b.C.Companion.bar //function from companion object
import b.C.Companion.cValue //property from companion object
import b.<!UNRESOLVED_REFERENCE!>constant<!>.<!DEBUG_INFO_MISSING_UNRESOLVED!>fff<!> //function from val
import b.<!UNRESOLVED_REFERENCE!>constant<!>.<!DEBUG_INFO_MISSING_UNRESOLVED!>dValue<!> //property from val
import <!UNRESOLVED_REFERENCE!>smth<!>.<!DEBUG_INFO_MISSING_UNRESOLVED!>illegal<!>
import b.C.<!UNRESOLVED_REFERENCE!>smth<!>.<!DEBUG_INFO_MISSING_UNRESOLVED!>illegal<!>
<!OI;SYNTAX!><<!><!OI;SYNTAX!><<!><!OI;SYNTAX!><<!><!OI;SYNTAX!>HEAD<!><!OI;SYNTAX!><!>
import b.<!UNRESOLVED_REFERENCE!>bar<!>.<!DEBUG_INFO_MISSING_UNRESOLVED!>smth<!>
import b.<!UNRESOLVED_REFERENCE!>bar<!>.*
import b.<!UNRESOLVED_REFERENCE!>unr<!>.<!DEBUG_INFO_MISSING_UNRESOLVED!>unr<!>.<!DEBUG_INFO_MISSING_UNRESOLVED!>unr<!>
import <!UNRESOLVED_REFERENCE!>unr<!>.<!DEBUG_INFO_MISSING_UNRESOLVED!>unr<!>.<!DEBUG_INFO_MISSING_UNRESOLVED!>unr<!>
import b.constant
import b.E.Companion.f //val from companion object
fun test(arg: B) {
foo(value)
arg.ext()
bar()
foo(cValue)
<!UNRESOLVED_REFERENCE!>fff<!>(<!UNRESOLVED_REFERENCE!>dValue<!>)
constant.fff(constant.dValue)
f.f()
}
// FILE:b.kt
package b
class B() {}
fun foo(i: Int) = i
fun B.ext() {}
val value = 0
class C() {
companion object {
fun bar() {}
val cValue = 1
}
}
class D() {
fun fff(s: String) = s
val dValue = "w"
}
val constant = D()
class E() {
companion object {
val f = F()
}
}
class F() {
fun f() {}
}
fun bar() {}
//FILE:c.kt
package c
import c.<!CANNOT_ALL_UNDER_IMPORT_FROM_SINGLETON!>C<!>.*
object C {
fun f() {
}
val i = 348
}
fun foo() {
if (<!UNRESOLVED_REFERENCE!>i<!> <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>==<!> 3) <!UNRESOLVED_REFERENCE!>f<!>()
}
//FILE:d.kt
package d
import d.A.Companion.B
import d.A.Companion.C
val b : B = B()
val c : B = C
class A() {
companion object {
open class B() {}
object C : B() {}
}
}
@@ -0,0 +1,114 @@
package
package a {
public fun test(/*0*/ arg: b.B): kotlin.Unit
}
package b {
public val constant: b.D
public val value: kotlin.Int = 0
public fun bar(): kotlin.Unit
public fun foo(/*0*/ i: kotlin.Int): kotlin.Int
public fun b.B.ext(): kotlin.Unit
public final class B {
public constructor B()
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
}
public final class C {
public constructor C()
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
public companion object Companion {
private constructor Companion()
public final val cValue: kotlin.Int = 1
public final fun bar(): kotlin.Unit
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
}
}
public final class D {
public constructor D()
public final val dValue: kotlin.String = "w"
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final fun fff(/*0*/ s: kotlin.String): kotlin.String
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class E {
public constructor E()
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
public companion object Companion {
private constructor Companion()
public final val f: b.F
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
}
}
public final class F {
public constructor F()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final fun f(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
package c {
public fun foo(): kotlin.Unit
public object C {
private constructor C()
public final val i: kotlin.Int = 348
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final fun f(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
package d {
public val b: d.A.Companion.B
public val c: d.A.Companion.B
public final class A {
public constructor A()
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
public companion object Companion {
private constructor Companion()
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
public open class B {
public constructor B()
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
}
public object C : d.A.Companion.B {
private constructor C()
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
}
}
}
}
+8 -5
View File
@@ -14,9 +14,12 @@ KtFile: FileStart_ERR.kt
PsiElement(DOT)('.')
PsiErrorElement:Expecting a top level declaration
PsiElement(IDENTIFIER)('bar')
PsiErrorElement:imports are only allowed in the beginning of file
<empty list>
PsiWhiteSpace('\n')
PsiErrorElement:Expecting a top level declaration
PsiElement(IDENTIFIER)('import')
PsiWhiteSpace(' ')
PsiErrorElement:Expecting a top level declaration
PsiElement(IDENTIFIER)('foo')
IMPORT_LIST
IMPORT_DIRECTIVE
PsiElement(import)('import')
PsiWhiteSpace(' ')
REFERENCE_EXPRESSION
PsiElement(IDENTIFIER)('foo')
+14
View File
@@ -0,0 +1,14 @@
package a
import a1
<<<HEAD
import a2
import a3
>>> BAR
fun bar() {}
import a2
import a4.df
fun foo() {}
+90
View File
@@ -0,0 +1,90 @@
KtFile: importsWithConflict.kt
PACKAGE_DIRECTIVE
PsiElement(package)('package')
PsiWhiteSpace(' ')
REFERENCE_EXPRESSION
PsiElement(IDENTIFIER)('a')
PsiWhiteSpace('\n\n')
IMPORT_LIST
IMPORT_DIRECTIVE
PsiElement(import)('import')
PsiWhiteSpace(' ')
REFERENCE_EXPRESSION
PsiElement(IDENTIFIER)('a1')
PsiWhiteSpace('\n')
PsiErrorElement:Expecting a top level declaration
PsiElement(LT)('<')
PsiErrorElement:Expecting a top level declaration
PsiElement(LT)('<')
PsiErrorElement:Expecting a top level declaration
PsiElement(LT)('<')
PsiErrorElement:Expecting a top level declaration
PsiElement(IDENTIFIER)('HEAD')
PsiErrorElement:imports are only allowed in the beginning of file
<empty list>
PsiWhiteSpace('\n')
IMPORT_LIST
IMPORT_DIRECTIVE
PsiElement(import)('import')
PsiWhiteSpace(' ')
REFERENCE_EXPRESSION
PsiElement(IDENTIFIER)('a2')
PsiWhiteSpace('\n')
IMPORT_DIRECTIVE
PsiElement(import)('import')
PsiWhiteSpace(' ')
REFERENCE_EXPRESSION
PsiElement(IDENTIFIER)('a3')
PsiWhiteSpace('\n')
PsiErrorElement:Expecting a top level declaration
PsiElement(GT)('>')
PsiErrorElement:Expecting a top level declaration
PsiElement(GT)('>')
PsiErrorElement:Expecting a top level declaration
PsiElement(GT)('>')
PsiWhiteSpace(' ')
PsiErrorElement:Expecting a top level declaration
PsiElement(IDENTIFIER)('BAR')
PsiWhiteSpace('\n\n')
FUN
PsiElement(fun)('fun')
PsiWhiteSpace(' ')
PsiElement(IDENTIFIER)('bar')
VALUE_PARAMETER_LIST
PsiElement(LPAR)('(')
PsiElement(RPAR)(')')
PsiWhiteSpace(' ')
BLOCK
PsiElement(LBRACE)('{')
PsiElement(RBRACE)('}')
PsiErrorElement:imports are only allowed in the beginning of file
<empty list>
PsiWhiteSpace('\n\n')
IMPORT_LIST
IMPORT_DIRECTIVE
PsiElement(import)('import')
PsiWhiteSpace(' ')
REFERENCE_EXPRESSION
PsiElement(IDENTIFIER)('a2')
PsiWhiteSpace('\n')
IMPORT_DIRECTIVE
PsiElement(import)('import')
PsiWhiteSpace(' ')
DOT_QUALIFIED_EXPRESSION
REFERENCE_EXPRESSION
PsiElement(IDENTIFIER)('a4')
PsiElement(DOT)('.')
REFERENCE_EXPRESSION
PsiElement(IDENTIFIER)('df')
PsiWhiteSpace('\n\n')
FUN
PsiElement(fun)('fun')
PsiWhiteSpace(' ')
PsiElement(IDENTIFIER)('foo')
VALUE_PARAMETER_LIST
PsiElement(LPAR)('(')
PsiElement(RPAR)(')')
PsiWhiteSpace(' ')
BLOCK
PsiElement(LBRACE)('{')
PsiElement(RBRACE)('}')
@@ -9071,6 +9071,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
runTest("compiler/testData/diagnostics/tests/imports/TopLevelClassVsPackage.kt");
}
@TestMetadata("twoImportLists.kt")
public void testTwoImportLists() throws Exception {
runTest("compiler/testData/diagnostics/tests/imports/twoImportLists.kt");
}
@TestMetadata("WrongImport.kt")
public void testWrongImport() throws Exception {
runTest("compiler/testData/diagnostics/tests/imports/WrongImport.kt");
@@ -9071,6 +9071,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
runTest("compiler/testData/diagnostics/tests/imports/TopLevelClassVsPackage.kt");
}
@TestMetadata("twoImportLists.kt")
public void testTwoImportLists() throws Exception {
runTest("compiler/testData/diagnostics/tests/imports/twoImportLists.kt");
}
@TestMetadata("WrongImport.kt")
public void testWrongImport() throws Exception {
runTest("compiler/testData/diagnostics/tests/imports/WrongImport.kt");
@@ -2013,6 +2013,11 @@ public class ParsingTestGenerated extends AbstractParsingTest {
runTest("compiler/testData/psi/recovery/ImportRecovery.kt");
}
@TestMetadata("importsWithConflict.kt")
public void testImportsWithConflict() throws Exception {
runTest("compiler/testData/psi/recovery/importsWithConflict.kt");
}
@TestMetadata("IncompleteAccessor1.kt")
public void testIncompleteAccessor1() throws Exception {
runTest("compiler/testData/psi/recovery/IncompleteAccessor1.kt");