Don't build light classes for local classes with parse errors (KT-24323, EA-107235)
Also use checks for building light classes in `getLightClassDataHolder`.
This commit is contained in:
+55
-25
@@ -26,6 +26,7 @@ import com.intellij.psi.*
|
|||||||
import com.intellij.psi.impl.PsiSubstitutorImpl
|
import com.intellij.psi.impl.PsiSubstitutorImpl
|
||||||
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
|
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
|
||||||
import com.intellij.psi.impl.source.PsiImmediateClassType
|
import com.intellij.psi.impl.source.PsiImmediateClassType
|
||||||
|
import com.intellij.psi.impl.source.tree.TreeUtil
|
||||||
import com.intellij.psi.scope.PsiScopeProcessor
|
import com.intellij.psi.scope.PsiScopeProcessor
|
||||||
import com.intellij.psi.search.SearchScope
|
import com.intellij.psi.search.SearchScope
|
||||||
import com.intellij.psi.stubs.IStubElementType
|
import com.intellij.psi.stubs.IStubElementType
|
||||||
@@ -34,6 +35,7 @@ import com.intellij.psi.util.CachedValue
|
|||||||
import com.intellij.psi.util.CachedValueProvider
|
import com.intellij.psi.util.CachedValueProvider
|
||||||
import com.intellij.psi.util.CachedValuesManager
|
import com.intellij.psi.util.CachedValuesManager
|
||||||
import com.intellij.psi.util.PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT
|
import com.intellij.psi.util.PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT
|
||||||
|
import com.intellij.psi.util.PsiUtilCore
|
||||||
import com.intellij.util.IncorrectOperationException
|
import com.intellij.util.IncorrectOperationException
|
||||||
import com.intellij.util.containers.ContainerUtil
|
import com.intellij.util.containers.ContainerUtil
|
||||||
import org.jetbrains.annotations.NonNls
|
import org.jetbrains.annotations.NonNls
|
||||||
@@ -345,34 +347,23 @@ abstract class KtLightClassForSourceDeclaration(protected val classOrObject: KtC
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (classOrObject is KtObjectDeclaration && classOrObject.isObjectLiteral()) {
|
return when {
|
||||||
if (classOrObject.containingFile.virtualFile == null) return null
|
classOrObject is KtObjectDeclaration && classOrObject.isObjectLiteral() ->
|
||||||
|
KtLightClassForAnonymousDeclaration(classOrObject)
|
||||||
|
|
||||||
return KtLightClassForAnonymousDeclaration(classOrObject)
|
classOrObject.isLocal ->
|
||||||
|
KtLightClassForLocalDeclaration(classOrObject)
|
||||||
|
|
||||||
|
else ->
|
||||||
|
KtLightClassImpl(classOrObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isEnumEntryWithoutBody(classOrObject)) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (classOrObject.isLocal) {
|
|
||||||
if (classOrObject.containingFile.virtualFile == null) return null
|
|
||||||
|
|
||||||
return KtLightClassForLocalDeclaration(classOrObject)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return KtLightClassImpl(classOrObject)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isEnumEntryWithoutBody(classOrObject: KtClassOrObject): Boolean {
|
|
||||||
if (classOrObject !is KtEnumEntry) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return classOrObject.getBody()?.declarations?.isEmpty() ?: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLightClassDataHolder(classOrObject: KtClassOrObject): LightClassDataHolder.ForClass {
|
fun getLightClassDataHolder(classOrObject: KtClassOrObject): LightClassDataHolder.ForClass {
|
||||||
|
if (classOrObject.shouldNotBeVisibleAsLightClass()) {
|
||||||
|
return InvalidLightClassDataHolder
|
||||||
|
}
|
||||||
|
|
||||||
return classOrObject.containingKtFile.script?.let { KtLightClassForScript.getLightClassCachedValue(it).value } ?:
|
return classOrObject.containingKtFile.script?.let { KtLightClassForScript.getLightClassCachedValue(it).value } ?:
|
||||||
getLightClassCachedValue(classOrObject).value
|
getLightClassCachedValue(classOrObject).value
|
||||||
}
|
}
|
||||||
@@ -516,5 +507,44 @@ fun KtClassOrObject.defaultJavaAncestorQualifiedName(): String? {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun KtClassOrObject.shouldNotBeVisibleAsLightClass() =
|
fun KtClassOrObject.shouldNotBeVisibleAsLightClass(): Boolean {
|
||||||
parentsWithSelf.filterIsInstance<KtClassOrObject>().any { it.hasExpectModifier() }
|
if (parentsWithSelf.filterIsInstance<KtClassOrObject>().any { it.hasExpectModifier() }) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLocal) {
|
||||||
|
if (containingFile.virtualFile == null) return true
|
||||||
|
if (hasParseErrorsAround(this) || PsiUtilCore.hasErrorElementChild(this)) return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEnumEntryWithoutBody(this)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isEnumEntryWithoutBody(classOrObject: KtClassOrObject): Boolean {
|
||||||
|
if (classOrObject !is KtEnumEntry) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return classOrObject.getBody()?.declarations?.isEmpty() ?: true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hasParseErrorsAround(psi: PsiElement): Boolean {
|
||||||
|
val node = psi.node ?: return false
|
||||||
|
|
||||||
|
TreeUtil.nextLeaf(node)?.let { nextLeaf ->
|
||||||
|
if (nextLeaf.elementType == TokenType.ERROR_ELEMENT || nextLeaf.treePrev?.elementType == TokenType.ERROR_ELEMENT) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeUtil.prevLeaf(node)?.let { prevLeaf ->
|
||||||
|
if (prevLeaf.elementType == TokenType.ERROR_ELEMENT || prevLeaf.treeNext?.elementType == TokenType.ERROR_ELEMENT) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
// NOT_GENERATED
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// Validation
|
||||||
|
// ALLOW_FRONTEND_EXCEPTION
|
||||||
|
// See KT-24323
|
||||||
|
|
||||||
|
@Retention(AnnotationRetention.RUNTIME /* ) was removed */
|
||||||
|
annotation class Validation()
|
||||||
|
|
||||||
+40
-11
@@ -23,7 +23,10 @@ import org.jetbrains.kotlin.checkers.KotlinMultiFileTestWithJava
|
|||||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.test.ConfigurationKind
|
import org.jetbrains.kotlin.test.ConfigurationKind
|
||||||
|
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||||
|
import org.jetbrains.kotlin.util.KotlinFrontEndException
|
||||||
|
import org.junit.Assert
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
abstract class AbstractCompilerLightClassTest : KotlinMultiFileTestWithJava<Void, Void>() {
|
abstract class AbstractCompilerLightClassTest : KotlinMultiFileTestWithJava<Void, Void>() {
|
||||||
@@ -34,11 +37,13 @@ abstract class AbstractCompilerLightClassTest : KotlinMultiFileTestWithJava<Void
|
|||||||
override fun doMultiFileTest(file: File, modules: MutableMap<String, ModuleAndDependencies>, files: MutableList<Void>) {
|
override fun doMultiFileTest(file: File, modules: MutableMap<String, ModuleAndDependencies>, files: MutableList<Void>) {
|
||||||
val environment = createEnvironment(file)
|
val environment = createEnvironment(file)
|
||||||
val expectedFile = KotlinTestUtils.replaceExtension(file, "java")
|
val expectedFile = KotlinTestUtils.replaceExtension(file, "java")
|
||||||
|
val allowFrontendExceptions = InTextDirectivesUtils.isDirectiveDefined(file.readText(), "// ALLOW_FRONTEND_EXCEPTION")
|
||||||
|
|
||||||
LightClassTestCommon.testLightClass(
|
LightClassTestCommon.testLightClass(
|
||||||
expectedFile,
|
expectedFile,
|
||||||
file,
|
file,
|
||||||
{ fqname -> findLightClass(environment, fqname) },
|
{ fqname -> findLightClass(allowFrontendExceptions, environment, fqname) },
|
||||||
LightClassTestCommon::removeEmptyDefaultImpls
|
LightClassTestCommon::removeEmptyDefaultImpls
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,17 +52,41 @@ abstract class AbstractCompilerLightClassTest : KotlinMultiFileTestWithJava<Void
|
|||||||
override fun createTestFile(module: Void?, fileName: String, text: String, directives: Map<String, String>): Void? = null
|
override fun createTestFile(module: Void?, fileName: String, text: String, directives: Map<String, String>): Void? = null
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun findLightClass(environment: KotlinCoreEnvironment, fqname: String): PsiClass? {
|
fun findLightClass(allowFrontendExceptions: Boolean, environment: KotlinCoreEnvironment, fqname: String): PsiClass? {
|
||||||
KotlinTestUtils.resolveAllKotlinFiles(environment)
|
assertException(allowFrontendExceptions, KotlinFrontEndException::class.java) {
|
||||||
|
KotlinTestUtils.resolveAllKotlinFiles(environment)
|
||||||
|
}
|
||||||
|
|
||||||
val lightCLassForScript = KotlinAsJavaSupport
|
val lightCLassForScript = KotlinAsJavaSupport
|
||||||
.getInstance(environment.project)
|
.getInstance(environment.project)
|
||||||
.getScriptClasses(FqName(fqname), GlobalSearchScope.allScope(environment.project))
|
.getScriptClasses(FqName(fqname), GlobalSearchScope.allScope(environment.project))
|
||||||
.firstOrNull()
|
.firstOrNull()
|
||||||
|
|
||||||
return lightCLassForScript ?: JavaElementFinder
|
return lightCLassForScript ?: JavaElementFinder
|
||||||
.getInstance(environment.project)
|
.getInstance(environment.project)
|
||||||
.findClass(fqname, GlobalSearchScope.allScope(environment.project))
|
.findClass(fqname, GlobalSearchScope.allScope(environment.project))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertException(shouldOccur: Boolean, klass: Class<out Throwable>, f: () -> Unit) {
|
||||||
|
if (!shouldOccur) {
|
||||||
|
f()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var wasThrown = false
|
||||||
|
try {
|
||||||
|
f()
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
wasThrown = true
|
||||||
|
|
||||||
|
if (!shouldOccur || !klass.isAssignableFrom(e.javaClass)) {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (shouldOccur && !wasThrown) {
|
||||||
|
Assert.fail("Expected exception wasn't thrown")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
@@ -191,6 +191,11 @@ public class CompilerLightClassTestGenerated extends AbstractCompilerLightClassT
|
|||||||
runTest("compiler/testData/asJava/lightClasses/compilationErrors/JvmPackageName.kt");
|
runTest("compiler/testData/asJava/lightClasses/compilationErrors/JvmPackageName.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("LocalInAnnotation.kt")
|
||||||
|
public void testLocalInAnnotation() throws Exception {
|
||||||
|
runTest("compiler/testData/asJava/lightClasses/compilationErrors/LocalInAnnotation.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("PrivateInTrait.kt")
|
@TestMetadata("PrivateInTrait.kt")
|
||||||
public void testPrivateInTrait() throws Exception {
|
public void testPrivateInTrait() throws Exception {
|
||||||
runTest("compiler/testData/asJava/lightClasses/compilationErrors/PrivateInTrait.kt");
|
runTest("compiler/testData/asJava/lightClasses/compilationErrors/PrivateInTrait.kt");
|
||||||
|
|||||||
+5
@@ -191,6 +191,11 @@ public class IdeLightClassTestGenerated extends AbstractIdeLightClassTest {
|
|||||||
runTest("compiler/testData/asJava/lightClasses/compilationErrors/JvmPackageName.kt");
|
runTest("compiler/testData/asJava/lightClasses/compilationErrors/JvmPackageName.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("LocalInAnnotation.kt")
|
||||||
|
public void testLocalInAnnotation() throws Exception {
|
||||||
|
runTest("compiler/testData/asJava/lightClasses/compilationErrors/LocalInAnnotation.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("PrivateInTrait.kt")
|
@TestMetadata("PrivateInTrait.kt")
|
||||||
public void testPrivateInTrait() throws Exception {
|
public void testPrivateInTrait() throws Exception {
|
||||||
runTest("compiler/testData/asJava/lightClasses/compilationErrors/PrivateInTrait.kt");
|
runTest("compiler/testData/asJava/lightClasses/compilationErrors/PrivateInTrait.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user