diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForSourceDeclaration.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForSourceDeclaration.kt index 07b9033891c..602cc67bf09 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForSourceDeclaration.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForSourceDeclaration.kt @@ -26,6 +26,7 @@ import com.intellij.psi.* import com.intellij.psi.impl.PsiSubstitutorImpl import com.intellij.psi.impl.java.stubs.PsiJavaFileStub 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.search.SearchScope 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.CachedValuesManager 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.containers.ContainerUtil import org.jetbrains.annotations.NonNls @@ -345,34 +347,23 @@ abstract class KtLightClassForSourceDeclaration(protected val classOrObject: KtC return null } - if (classOrObject is KtObjectDeclaration && classOrObject.isObjectLiteral()) { - if (classOrObject.containingFile.virtualFile == null) return null + return when { + 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 { + if (classOrObject.shouldNotBeVisibleAsLightClass()) { + return InvalidLightClassDataHolder + } + return classOrObject.containingKtFile.script?.let { KtLightClassForScript.getLightClassCachedValue(it).value } ?: getLightClassCachedValue(classOrObject).value } @@ -516,5 +507,44 @@ fun KtClassOrObject.defaultJavaAncestorQualifiedName(): String? { } } -fun KtClassOrObject.shouldNotBeVisibleAsLightClass() = - parentsWithSelf.filterIsInstance().any { it.hasExpectModifier() } +fun KtClassOrObject.shouldNotBeVisibleAsLightClass(): Boolean { + if (parentsWithSelf.filterIsInstance().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 +} diff --git a/compiler/testData/asJava/lightClasses/compilationErrors/LocalInAnnotation.java b/compiler/testData/asJava/lightClasses/compilationErrors/LocalInAnnotation.java new file mode 100644 index 00000000000..1e1bc72eb77 --- /dev/null +++ b/compiler/testData/asJava/lightClasses/compilationErrors/LocalInAnnotation.java @@ -0,0 +1 @@ +// NOT_GENERATED diff --git a/compiler/testData/asJava/lightClasses/compilationErrors/LocalInAnnotation.kt b/compiler/testData/asJava/lightClasses/compilationErrors/LocalInAnnotation.kt new file mode 100644 index 00000000000..8f941b76dd6 --- /dev/null +++ b/compiler/testData/asJava/lightClasses/compilationErrors/LocalInAnnotation.kt @@ -0,0 +1,7 @@ +// Validation +// ALLOW_FRONTEND_EXCEPTION +// See KT-24323 + +@Retention(AnnotationRetention.RUNTIME /* ) was removed */ + annotation class Validation() + diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/asJava/AbstractCompilerLightClassTest.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/asJava/AbstractCompilerLightClassTest.kt index bd0a32dae55..1781da9c890 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/asJava/AbstractCompilerLightClassTest.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/asJava/AbstractCompilerLightClassTest.kt @@ -23,7 +23,10 @@ import org.jetbrains.kotlin.checkers.KotlinMultiFileTestWithJava import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.test.ConfigurationKind +import org.jetbrains.kotlin.test.InTextDirectivesUtils import org.jetbrains.kotlin.test.KotlinTestUtils +import org.jetbrains.kotlin.util.KotlinFrontEndException +import org.junit.Assert import java.io.File abstract class AbstractCompilerLightClassTest : KotlinMultiFileTestWithJava() { @@ -34,11 +37,13 @@ abstract class AbstractCompilerLightClassTest : KotlinMultiFileTestWithJava, files: MutableList) { val environment = createEnvironment(file) val expectedFile = KotlinTestUtils.replaceExtension(file, "java") + val allowFrontendExceptions = InTextDirectivesUtils.isDirectiveDefined(file.readText(), "// ALLOW_FRONTEND_EXCEPTION") + LightClassTestCommon.testLightClass( - expectedFile, - file, - { fqname -> findLightClass(environment, fqname) }, - LightClassTestCommon::removeEmptyDefaultImpls + expectedFile, + file, + { fqname -> findLightClass(allowFrontendExceptions, environment, fqname) }, + LightClassTestCommon::removeEmptyDefaultImpls ) } @@ -47,17 +52,41 @@ abstract class AbstractCompilerLightClassTest : KotlinMultiFileTestWithJava): Void? = null companion object { - fun findLightClass(environment: KotlinCoreEnvironment, fqname: String): PsiClass? { - KotlinTestUtils.resolveAllKotlinFiles(environment) + fun findLightClass(allowFrontendExceptions: Boolean, environment: KotlinCoreEnvironment, fqname: String): PsiClass? { + assertException(allowFrontendExceptions, KotlinFrontEndException::class.java) { + KotlinTestUtils.resolveAllKotlinFiles(environment) + } val lightCLassForScript = KotlinAsJavaSupport - .getInstance(environment.project) - .getScriptClasses(FqName(fqname), GlobalSearchScope.allScope(environment.project)) - .firstOrNull() + .getInstance(environment.project) + .getScriptClasses(FqName(fqname), GlobalSearchScope.allScope(environment.project)) + .firstOrNull() return lightCLassForScript ?: JavaElementFinder - .getInstance(environment.project) - .findClass(fqname, GlobalSearchScope.allScope(environment.project)) + .getInstance(environment.project) + .findClass(fqname, GlobalSearchScope.allScope(environment.project)) + } + + private fun assertException(shouldOccur: Boolean, klass: Class, 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") + } + } } } } diff --git a/compiler/tests/org/jetbrains/kotlin/asJava/CompilerLightClassTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/asJava/CompilerLightClassTestGenerated.java index b3bac63436d..402acd05aa0 100644 --- a/compiler/tests/org/jetbrains/kotlin/asJava/CompilerLightClassTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/asJava/CompilerLightClassTestGenerated.java @@ -191,6 +191,11 @@ public class CompilerLightClassTestGenerated extends AbstractCompilerLightClassT 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") public void testPrivateInTrait() throws Exception { runTest("compiler/testData/asJava/lightClasses/compilationErrors/PrivateInTrait.kt"); diff --git a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/IdeLightClassTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/IdeLightClassTestGenerated.java index 4b7fe36a89f..c71b80a6929 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/IdeLightClassTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/IdeLightClassTestGenerated.java @@ -191,6 +191,11 @@ public class IdeLightClassTestGenerated extends AbstractIdeLightClassTest { 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") public void testPrivateInTrait() throws Exception { runTest("compiler/testData/asJava/lightClasses/compilationErrors/PrivateInTrait.kt");