diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassDataProvider.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassDataProvider.kt index 41d0d9e1919..e568ad505fe 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassDataProvider.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassDataProvider.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.asJava import com.google.common.collect.Lists +import com.intellij.openapi.components.ServiceManager import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.project.Project @@ -107,6 +108,8 @@ abstract class LightClassDataProvider( if (pop !== javaFileStub) { LOG.error("Unbalanced stack operations: " + pop) } + + ServiceManager.getService(project, StubComputationTracker::class.java)?.onStubComputed(javaFileStub) } catch (e: ProcessCanceledException) { throw e @@ -354,3 +357,8 @@ class LightClassDataProviderForFileFacade( return this.javaClass.name + " for $facadeFqName" } } + + +interface StubComputationTracker { + fun onStubComputed(javaFileStub: PsiJavaFileStub) +} diff --git a/idea/idea-completion/testData/injava/AnnotationParameter.java b/idea/idea-completion/testData/injava/AnnotationParameter.java index b43ba81d2a9..561eb1d0c64 100644 --- a/idea/idea-completion/testData/injava/AnnotationParameter.java +++ b/idea/idea-completion/testData/injava/AnnotationParameter.java @@ -4,3 +4,4 @@ public class Testing { // EXIST: c // EXIST: d +// LIGHT_CLASS: mockLib.foo.Anno \ No newline at end of file diff --git a/idea/idea-completion/testData/injava/ClassMembers.java b/idea/idea-completion/testData/injava/ClassMembers.java index dd50d321a84..2e7ce2f56e0 100644 --- a/idea/idea-completion/testData/injava/ClassMembers.java +++ b/idea/idea-completion/testData/injava/ClassMembers.java @@ -5,3 +5,4 @@ public class Testing { } // EXIST: foo +// LIGHT_CLASS: mockLib.foo.LibClass \ No newline at end of file diff --git a/idea/idea-completion/testData/injava/ClassObject.java b/idea/idea-completion/testData/injava/ClassObject.java index a51dc953597..3926a27d0bb 100644 --- a/idea/idea-completion/testData/injava/ClassObject.java +++ b/idea/idea-completion/testData/injava/ClassObject.java @@ -4,4 +4,5 @@ public class Testing { } } -// EXIST: classObjectFun \ No newline at end of file +// EXIST: classObjectFun +// LIGHT_CLASS: mockLib.foo.LibClass \ No newline at end of file diff --git a/idea/idea-completion/testData/injava/ClassObjectField.java b/idea/idea-completion/testData/injava/ClassObjectField.java index a5ae5f15cd8..94d9ec4a57e 100644 --- a/idea/idea-completion/testData/injava/ClassObjectField.java +++ b/idea/idea-completion/testData/injava/ClassObjectField.java @@ -5,3 +5,4 @@ public class Testing { } // EXIST: Companion +// LIGHT_CLASS: mockLib.foo.LibClass \ No newline at end of file diff --git a/idea/idea-completion/testData/injava/EnumConstants.java b/idea/idea-completion/testData/injava/EnumConstants.java index c112f6127fe..af38bc78d49 100644 --- a/idea/idea-completion/testData/injava/EnumConstants.java +++ b/idea/idea-completion/testData/injava/EnumConstants.java @@ -5,3 +5,4 @@ public class Testing { } // EXIST: RED, GREEN, BLUE +// LIGHT_CLASS: mockLib.foo.LibEnum \ No newline at end of file diff --git a/idea/idea-completion/testData/injava/InterfaceDefaultImpl.java b/idea/idea-completion/testData/injava/InterfaceDefaultImpl.java index bbdce551935..aa763f2c87c 100644 --- a/idea/idea-completion/testData/injava/InterfaceDefaultImpl.java +++ b/idea/idea-completion/testData/injava/InterfaceDefaultImpl.java @@ -5,3 +5,4 @@ public class Testing { } // EXIST: DefaultImpls +// LIGHT_CLASS: mockLib.foo.MyInterface \ No newline at end of file diff --git a/idea/idea-completion/testData/injava/InterfaceDefaultImplImportedMembers.java b/idea/idea-completion/testData/injava/InterfaceDefaultImplImportedMembers.java index a201edca4c1..9c03b65a358 100644 --- a/idea/idea-completion/testData/injava/InterfaceDefaultImplImportedMembers.java +++ b/idea/idea-completion/testData/injava/InterfaceDefaultImplImportedMembers.java @@ -6,3 +6,4 @@ public class Testing { } // EXIST: foo +// LIGHT_CLASS: mockLib.foo.MyInterface \ No newline at end of file diff --git a/idea/idea-completion/testData/injava/InterfaceDefaultImplMembers.java b/idea/idea-completion/testData/injava/InterfaceDefaultImplMembers.java index 8bb70e273f1..b9532f7a1b9 100644 --- a/idea/idea-completion/testData/injava/InterfaceDefaultImplMembers.java +++ b/idea/idea-completion/testData/injava/InterfaceDefaultImplMembers.java @@ -5,3 +5,4 @@ public class Testing { } // EXIST: foo +// LIGHT_CLASS: mockLib.foo.MyInterface \ No newline at end of file diff --git a/idea/idea-completion/testData/injava/InterfaceDefaultImplStaticImportedMembers.java b/idea/idea-completion/testData/injava/InterfaceDefaultImplStaticImportedMembers.java index dcf64ba552b..75434ddd4c9 100644 --- a/idea/idea-completion/testData/injava/InterfaceDefaultImplStaticImportedMembers.java +++ b/idea/idea-completion/testData/injava/InterfaceDefaultImplStaticImportedMembers.java @@ -6,3 +6,4 @@ public class Testing { } // EXIST: foo +// LIGHT_CLASS: mockLib.foo.MyInterface \ No newline at end of file diff --git a/idea/idea-completion/testData/injava/MultiFileFacadeMembers.java b/idea/idea-completion/testData/injava/MultiFileFacadeMembers.java index 5baa5fa7296..b33cbce486d 100644 --- a/idea/idea-completion/testData/injava/MultiFileFacadeMembers.java +++ b/idea/idea-completion/testData/injava/MultiFileFacadeMembers.java @@ -6,4 +6,4 @@ public class Testing { // EXIST: funInFacade // EXIST: funInFacade2 - +// LIGHT_CLASS: facades.MultiFileFacadeClass \ No newline at end of file diff --git a/idea/idea-completion/testData/injava/Nested.java b/idea/idea-completion/testData/injava/Nested.java index 862967b5fc4..d42492f192c 100644 --- a/idea/idea-completion/testData/injava/Nested.java +++ b/idea/idea-completion/testData/injava/Nested.java @@ -7,3 +7,4 @@ public class Testing { } // EXIST: LibClass.Nested +// LIGHT_CLASS: mockLib.foo.LibClass \ No newline at end of file diff --git a/idea/idea-completion/testData/injava/NestedClassMembers.java b/idea/idea-completion/testData/injava/NestedClassMembers.java index 597cfbc069b..a1b33992531 100644 --- a/idea/idea-completion/testData/injava/NestedClassMembers.java +++ b/idea/idea-completion/testData/injava/NestedClassMembers.java @@ -6,3 +6,4 @@ public class Testing { // EXIST: getValInNested // EXIST: funInNested +// LIGHT_CLASS: mockLib.foo.LibClass \ No newline at end of file diff --git a/idea/idea-completion/testData/injava/NestedClassMembers2.java b/idea/idea-completion/testData/injava/NestedClassMembers2.java index 4c6846bf963..24faf0c9c10 100644 --- a/idea/idea-completion/testData/injava/NestedClassMembers2.java +++ b/idea/idea-completion/testData/injava/NestedClassMembers2.java @@ -6,3 +6,4 @@ public class Testing { // EXIST: getValInNested // EXIST: funInNested +// LIGHT_CLASS: mockLib.foo.LibClass \ No newline at end of file diff --git a/idea/idea-completion/testData/injava/NestedObjectInstance.java b/idea/idea-completion/testData/injava/NestedObjectInstance.java index 7073a3525da..f7e59fea21a 100644 --- a/idea/idea-completion/testData/injava/NestedObjectInstance.java +++ b/idea/idea-completion/testData/injava/NestedObjectInstance.java @@ -5,3 +5,4 @@ public class Testing { } // EXIST: INSTANCE +// LIGHT_CLASS: mockLib.foo.LibClass \ No newline at end of file diff --git a/idea/idea-completion/testData/injava/ObjectInClassObjects.java b/idea/idea-completion/testData/injava/ObjectInClassObjects.java index 43d8f18cfee..5d5f030611a 100644 --- a/idea/idea-completion/testData/injava/ObjectInClassObjects.java +++ b/idea/idea-completion/testData/injava/ObjectInClassObjects.java @@ -4,3 +4,4 @@ public class Testing { } // EXIST: F +// LIGHT_CLASS: mockLib.foo.F diff --git a/idea/idea-completion/testData/injava/ObjectInstance.java b/idea/idea-completion/testData/injava/ObjectInstance.java index 476188ddeeb..d30f9e16ac8 100644 --- a/idea/idea-completion/testData/injava/ObjectInstance.java +++ b/idea/idea-completion/testData/injava/ObjectInstance.java @@ -5,3 +5,4 @@ public class Testing { } // EXIST: INSTANCE +// LIGHT_CLASS: mockLib.foo.LibObject \ No newline at end of file diff --git a/idea/idea-completion/testData/injava/SingleFileFacadeMembers.java b/idea/idea-completion/testData/injava/SingleFileFacadeMembers.java index 04e0713583e..70080176d59 100644 --- a/idea/idea-completion/testData/injava/SingleFileFacadeMembers.java +++ b/idea/idea-completion/testData/injava/SingleFileFacadeMembers.java @@ -8,4 +8,4 @@ public class Testing { // EXIST: topLevelExtFunction // EXIST: getTopLevelVar // EXIST: setTopLevelVar - +// LIGHT_CLASS: mockLib.foo.MainKt diff --git a/idea/idea-completion/testData/injava/TopLevelMembers.java b/idea/idea-completion/testData/injava/TopLevelMembers.java index 123acc6fba2..9498d896910 100644 --- a/idea/idea-completion/testData/injava/TopLevelMembers.java +++ b/idea/idea-completion/testData/injava/TopLevelMembers.java @@ -7,3 +7,4 @@ public class Testing { // EXIST: topLevelFunction // EXIST: topLevelExtFunction // EXIST: getTopLevelVar, setTopLevelVar +// LIGHT_CLASS: mockLib.foo.MainKt \ No newline at end of file diff --git a/idea/idea-completion/testData/injava/TraitMember.java b/idea/idea-completion/testData/injava/TraitMember.java index 837615713e6..dd0b9e3894f 100644 --- a/idea/idea-completion/testData/injava/TraitMember.java +++ b/idea/idea-completion/testData/injava/TraitMember.java @@ -5,4 +5,4 @@ public class Testing { } // EXIST: foo - +// LIGHT_CLASS: mockLib.foo.LibTrait \ No newline at end of file diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/AbstractKotlinSourceInJavaCompletionTest.kt b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/AbstractKotlinSourceInJavaCompletionTest.kt index fd0bacacf33..9cd5c6add4c 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/AbstractKotlinSourceInJavaCompletionTest.kt +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/AbstractKotlinSourceInJavaCompletionTest.kt @@ -19,10 +19,6 @@ package org.jetbrains.kotlin.idea.completion.test import com.intellij.codeInsight.completion.CompletionType import com.intellij.openapi.util.io.FileUtil import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase -import com.intellij.util.ArrayUtil -import com.intellij.util.Function -import com.intellij.util.containers.ContainerUtil -import junit.framework.TestCase import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform import java.io.File @@ -31,17 +27,16 @@ public abstract class AbstractKotlinSourceInJavaCompletionTest : KotlinFixtureCo override fun doTest(testPath: String) { val mockLibDir = File(COMPLETION_TEST_DATA_BASE_PATH + "/injava/mockLib") - val listFiles = mockLibDir.listFiles() - TestCase.assertNotNull(listFiles) - val paths = ArrayUtil.toStringArray(ContainerUtil.map(listFiles, object : Function { - override fun `fun`(file: File): String { - return FileUtil.toSystemIndependentName(file.getAbsolutePath()) - } - })) + val paths = mockLibDir.listFiles()!!.map { + FileUtil.toSystemIndependentName(it.absolutePath) + }.toTypedArray() + myFixture.configureByFiles(*paths) - super.doTest(testPath) + LightClassComputationControl.testWithControl(project, FileUtil.loadFile(File(testPath))) { + super.doTest(testPath) + } } override fun getProjectDescriptor() = LightCodeInsightFixtureTestCase.JAVA_LATEST override fun defaultCompletionType() = CompletionType.BASIC -} +} \ No newline at end of file diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/ExpectedCompletionUtils.kt b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/ExpectedCompletionUtils.kt index b0abd518fc7..10a2a459396 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/ExpectedCompletionUtils.kt +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/ExpectedCompletionUtils.kt @@ -133,6 +133,7 @@ public object ExpectedCompletionUtils { NOTHING_ELSE_PREFIX, RUNTIME_TYPE, COMPLETION_TYPE_PREFIX, + LightClassComputationControl.LIGHT_CLASS_DIRECTIVE, AstAccessControl.ALLOW_AST_ACCESS_DIRECTIVE) public fun itemsShouldExist(fileText: String, platform: TargetPlatform?): Array { diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/LightClassComputationControl.kt b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/LightClassComputationControl.kt new file mode 100644 index 00000000000..1d40331ffeb --- /dev/null +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/LightClassComputationControl.kt @@ -0,0 +1,72 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.completion.test + +import com.intellij.openapi.components.ComponentManager +import com.intellij.openapi.project.Project +import com.intellij.psi.impl.java.stubs.PsiClassStub +import com.intellij.psi.impl.java.stubs.PsiJavaFileStub +import org.jetbrains.kotlin.asJava.StubComputationTracker +import org.jetbrains.kotlin.test.InTextDirectivesUtils +import org.junit.Assert +import org.picocontainer.MutablePicoContainer +import java.util.* + +object LightClassComputationControl { + fun testWithControl(project: Project, testText: String, testBody: () -> Unit) { + val expectedLightClassFqNames = InTextDirectivesUtils.findLinesWithPrefixesRemoved( + testText, "// $LIGHT_CLASS_DIRECTIVE" + ).map { it.trim() } + + val actualFqNames = ArrayList() + val stubComputationTracker = object : StubComputationTracker { + override fun onStubComputed(javaFileStub: PsiJavaFileStub) { + val qualifiedName = (javaFileStub.childrenStubs.single() as PsiClassStub<*>).qualifiedName!! + actualFqNames.add(qualifiedName) + } + } + + project.withServiceRegistered(stubComputationTracker) { + testBody() + } + + if (expectedLightClassFqNames.toSortedSet() != actualFqNames.toSortedSet()) { + Assert.fail( + "Expected to compute: ${expectedLightClassFqNames.prettyToString()}\n" + + "Actually computed: ${actualFqNames.prettyToString()}\n" + + "Use $LIGHT_CLASS_DIRECTIVE to specify expected light class computations" + ) + } + } + + val LIGHT_CLASS_DIRECTIVE = "LIGHT_CLASS:" + + private fun List.prettyToString() = if (isEmpty()) "" else joinToString() +} + +private inline fun ComponentManager.withServiceRegistered(instance: T, body: () -> Unit) { + val picoContainer = picoContainer as MutablePicoContainer + val key = T::class.java.name + try { + picoContainer.unregisterComponent(key) + picoContainer.registerComponentInstance(key, instance) + body() + } + finally { + picoContainer.unregisterComponent(key) + } +}