Load module annotations in IDE
#KT-22759 Fixed
This commit is contained in:
@@ -126,12 +126,12 @@ object MockLibraryUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun compileJsLibraryToJar(sourcesPath: String, jarName: String, addSources: Boolean): File {
|
fun compileJsLibraryToJar(sourcesPath: String, jarName: String, addSources: Boolean, extraOptions: List<String> = emptyList()): File {
|
||||||
val contentDir = KotlinTestUtils.tmpDir("testLibrary-" + jarName)
|
val contentDir = KotlinTestUtils.tmpDir("testLibrary-" + jarName)
|
||||||
|
|
||||||
val outDir = File(contentDir, "out")
|
val outDir = File(contentDir, "out")
|
||||||
val outputFile = File(outDir, jarName + ".js")
|
val outputFile = File(outDir, jarName + ".js")
|
||||||
compileKotlin2JS(sourcesPath, outputFile)
|
compileKotlin2JS(sourcesPath, outputFile, extraOptions)
|
||||||
|
|
||||||
return createJarFile(contentDir, outDir, jarName, sourcesPath.takeIf { addSources })
|
return createJarFile(contentDir, outDir, jarName, sourcesPath.takeIf { addSources })
|
||||||
}
|
}
|
||||||
@@ -189,8 +189,8 @@ object MockLibraryUtil {
|
|||||||
runJvmCompiler(args)
|
runJvmCompiler(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun compileKotlin2JS(sourcesPath: String, outputFile: File) {
|
private fun compileKotlin2JS(sourcesPath: String, outputFile: File, extraOptions: List<String>) {
|
||||||
runJsCompiler(listOf("-meta-info", "-output", outputFile.absolutePath, sourcesPath))
|
runJsCompiler(listOf("-meta-info", "-output", outputFile.absolutePath, sourcesPath, *extraOptions.toTypedArray()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun compileKotlinModule(buildFilePath: String) {
|
fun compileKotlinModule(buildFilePath: String) {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.name;
|
package org.jetbrains.kotlin.name;
|
||||||
|
|
||||||
|
import kotlin.text.StringsKt;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@@ -94,6 +95,16 @@ public final class ClassId {
|
|||||||
return packageFqName.startsWith(segment);
|
return packageFqName.startsWith(segment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string a string where packages are delimited by '/' and classes by '.', e.g. "kotlin/Map.Entry"
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public static ClassId fromString(@NotNull String string) {
|
||||||
|
String packageName = StringsKt.substringBeforeLast(string, '/', "").replace('/', '.');
|
||||||
|
String className = StringsKt.substringAfterLast(string, '/', string);
|
||||||
|
return new ClassId(new FqName(packageName), new FqName(className), false);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return a string where packages are delimited by '/' and classes by '.', e.g. "kotlin/Map.Entry"
|
* @return a string where packages are delimited by '/' and classes by '.', e.g. "kotlin/Map.Entry"
|
||||||
*/
|
*/
|
||||||
|
|||||||
+4
-3
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.idea.caches.resolve
|
|||||||
import com.intellij.psi.search.GlobalSearchScope
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
import com.intellij.util.indexing.FileBasedIndex
|
import com.intellij.util.indexing.FileBasedIndex
|
||||||
import org.jetbrains.kotlin.descriptors.PackagePartProvider
|
import org.jetbrains.kotlin.descriptors.PackagePartProvider
|
||||||
|
import org.jetbrains.kotlin.idea.vfilefinder.KotlinJvmModuleAnnotationsIndex
|
||||||
import org.jetbrains.kotlin.idea.vfilefinder.KotlinModuleMappingIndex
|
import org.jetbrains.kotlin.idea.vfilefinder.KotlinModuleMappingIndex
|
||||||
import org.jetbrains.kotlin.load.kotlin.PackageParts
|
import org.jetbrains.kotlin.load.kotlin.PackageParts
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
@@ -33,7 +34,7 @@ class IDEPackagePartProvider(val scope: GlobalSearchScope) : PackagePartProvider
|
|||||||
private fun getPackageParts(packageFqName: String): MutableList<PackageParts> =
|
private fun getPackageParts(packageFqName: String): MutableList<PackageParts> =
|
||||||
FileBasedIndex.getInstance().getValues(KotlinModuleMappingIndex.KEY, packageFqName, scope)
|
FileBasedIndex.getInstance().getValues(KotlinModuleMappingIndex.KEY, packageFqName, scope)
|
||||||
|
|
||||||
override fun getAnnotationsOnBinaryModule(moduleName: String): List<ClassId> {
|
// Note that in case of several modules with the same name, we return all annotations on all of them, which is probably incorrect
|
||||||
throw UnsupportedOperationException()
|
override fun getAnnotationsOnBinaryModule(moduleName: String): List<ClassId> =
|
||||||
}
|
FileBasedIndex.getInstance().getValues(KotlinJvmModuleAnnotationsIndex.KEY, moduleName, scope).flatten()
|
||||||
}
|
}
|
||||||
|
|||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||||
|
* that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.compiler
|
||||||
|
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.IDEPackagePartProvider
|
||||||
|
import org.jetbrains.kotlin.js.resolve.getAnnotationsOnContainingJsModule
|
||||||
|
import org.jetbrains.kotlin.load.kotlin.getJvmModuleNameForDeserializedDescriptor
|
||||||
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
|
import org.jetbrains.kotlin.resolve.ModuleAnnotationsResolver
|
||||||
|
|
||||||
|
class IdeModuleAnnotationsResolver(private val project: Project) : ModuleAnnotationsResolver {
|
||||||
|
override fun getAnnotationsOnContainingModule(descriptor: DeclarationDescriptor): List<ClassId> {
|
||||||
|
getAnnotationsOnContainingJsModule(descriptor)?.let { return it }
|
||||||
|
|
||||||
|
val moduleName = getJvmModuleNameForDeserializedDescriptor(descriptor) ?: return emptyList()
|
||||||
|
// TODO: allScope is incorrect here, need to look only in the root where this element comes from
|
||||||
|
return IDEPackagePartProvider(GlobalSearchScope.allScope(project)).getAnnotationsOnBinaryModule(moduleName)
|
||||||
|
}
|
||||||
|
}
|
||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||||
|
* that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.vfilefinder
|
||||||
|
|
||||||
|
import com.intellij.util.indexing.*
|
||||||
|
import com.intellij.util.io.DataExternalizer
|
||||||
|
import com.intellij.util.io.IOUtil
|
||||||
|
import org.jetbrains.kotlin.load.kotlin.ModuleMapping
|
||||||
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
|
import org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration
|
||||||
|
import java.io.DataInput
|
||||||
|
import java.io.DataOutput
|
||||||
|
|
||||||
|
object KotlinJvmModuleAnnotationsIndex : FileBasedIndexExtension<String, List<ClassId>>() {
|
||||||
|
|
||||||
|
val KEY: ID<String, List<ClassId>> = ID.create(KotlinJvmModuleAnnotationsIndex::class.java.canonicalName)
|
||||||
|
|
||||||
|
private val KEY_DESCRIPTOR = KotlinModuleMappingIndex.STRING_KEY_DESCRIPTOR
|
||||||
|
|
||||||
|
private val VALUE_EXTERNALIZER = object : DataExternalizer<List<ClassId>> {
|
||||||
|
override fun read(input: DataInput): List<ClassId>? =
|
||||||
|
IOUtil.readStringList(input).map(ClassId::fromString)
|
||||||
|
|
||||||
|
override fun save(out: DataOutput, value: List<ClassId>) =
|
||||||
|
IOUtil.writeStringList(out, value.map(ClassId::asString))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getName() = KEY
|
||||||
|
|
||||||
|
override fun dependsOnFileContent() = true
|
||||||
|
|
||||||
|
override fun getKeyDescriptor() = KEY_DESCRIPTOR
|
||||||
|
|
||||||
|
override fun getValueExternalizer() = VALUE_EXTERNALIZER
|
||||||
|
|
||||||
|
override fun getInputFilter(): FileBasedIndex.InputFilter =
|
||||||
|
FileBasedIndex.InputFilter { file -> file.extension == ModuleMapping.MAPPING_FILE_EXT }
|
||||||
|
|
||||||
|
override fun getVersion(): Int = 1
|
||||||
|
|
||||||
|
override fun getIndexer(): DataIndexer<String, List<ClassId>, FileContent> = DataIndexer { inputData ->
|
||||||
|
val file = inputData.file
|
||||||
|
try {
|
||||||
|
val moduleMapping = ModuleMapping.create(inputData.content, file.toString(), DeserializationConfiguration.Default)
|
||||||
|
return@DataIndexer mapOf(file.nameWithoutExtension to moduleMapping.moduleData.annotations)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// Exceptions are already reported in KotlinModuleMappingIndex
|
||||||
|
emptyMap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-2
@@ -30,7 +30,7 @@ object KotlinModuleMappingIndex : FileBasedIndexExtension<String, PackageParts>(
|
|||||||
|
|
||||||
val KEY: ID<String, PackageParts> = ID.create(KotlinModuleMappingIndex::class.java.canonicalName)
|
val KEY: ID<String, PackageParts> = ID.create(KotlinModuleMappingIndex::class.java.canonicalName)
|
||||||
|
|
||||||
private val KEY_DESCRIPTOR = object : KeyDescriptor<String> {
|
internal val STRING_KEY_DESCRIPTOR = object : KeyDescriptor<String> {
|
||||||
override fun save(output: DataOutput, value: String) = IOUtil.writeUTF(output, value)
|
override fun save(output: DataOutput, value: String) = IOUtil.writeUTF(output, value)
|
||||||
|
|
||||||
override fun read(input: DataInput) = IOUtil.readUTF(input)
|
override fun read(input: DataInput) = IOUtil.readUTF(input)
|
||||||
@@ -63,7 +63,7 @@ object KotlinModuleMappingIndex : FileBasedIndexExtension<String, PackageParts>(
|
|||||||
|
|
||||||
override fun dependsOnFileContent() = true
|
override fun dependsOnFileContent() = true
|
||||||
|
|
||||||
override fun getKeyDescriptor() = KEY_DESCRIPTOR
|
override fun getKeyDescriptor() = STRING_KEY_DESCRIPTOR
|
||||||
|
|
||||||
override fun getValueExternalizer() = VALUE_EXTERNALIZER
|
override fun getValueExternalizer() = VALUE_EXTERNALIZER
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -71,7 +71,7 @@ public class SdkAndMockLibraryProjectDescriptor extends KotlinLightProjectDescri
|
|||||||
List<String> extraOptions = allowKotlinPackage ? Collections.singletonList("-Xallow-kotlin-package") : emptyList();
|
List<String> extraOptions = allowKotlinPackage ? Collections.singletonList("-Xallow-kotlin-package") : emptyList();
|
||||||
File libraryJar =
|
File libraryJar =
|
||||||
isJsLibrary
|
isJsLibrary
|
||||||
? MockLibraryUtil.compileJsLibraryToJar(sourcesPath, LIBRARY_NAME, withSources)
|
? MockLibraryUtil.compileJsLibraryToJar(sourcesPath, LIBRARY_NAME, withSources, Collections.emptyList())
|
||||||
: MockLibraryUtil.compileJvmLibraryToJar(sourcesPath, LIBRARY_NAME, withSources, true, extraOptions, classpath);
|
: MockLibraryUtil.compileJvmLibraryToJar(sourcesPath, LIBRARY_NAME, withSources, true, extraOptions, classpath);
|
||||||
String jarUrl = getJarUrl(libraryJar);
|
String jarUrl = getJarUrl(libraryJar);
|
||||||
|
|
||||||
|
|||||||
@@ -247,6 +247,9 @@
|
|||||||
<projectService serviceInterface="org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver"
|
<projectService serviceInterface="org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver"
|
||||||
serviceImplementation="org.jetbrains.kotlin.idea.modules.IdeJavaModuleResolver"/>
|
serviceImplementation="org.jetbrains.kotlin.idea.modules.IdeJavaModuleResolver"/>
|
||||||
|
|
||||||
|
<projectService serviceInterface="org.jetbrains.kotlin.resolve.ModuleAnnotationsResolver"
|
||||||
|
serviceImplementation="org.jetbrains.kotlin.idea.compiler.IdeModuleAnnotationsResolver"/>
|
||||||
|
|
||||||
<projectService serviceInterface="org.jetbrains.kotlin.asJava.LightClassGenerationSupport"
|
<projectService serviceInterface="org.jetbrains.kotlin.asJava.LightClassGenerationSupport"
|
||||||
serviceImplementation="org.jetbrains.kotlin.idea.caches.resolve.IDELightClassGenerationSupport"/>
|
serviceImplementation="org.jetbrains.kotlin.idea.caches.resolve.IDELightClassGenerationSupport"/>
|
||||||
|
|
||||||
@@ -687,6 +690,7 @@
|
|||||||
<fileBasedIndex implementation="org.jetbrains.kotlin.idea.vfilefinder.KotlinMetadataFilePackageIndex"/>
|
<fileBasedIndex implementation="org.jetbrains.kotlin.idea.vfilefinder.KotlinMetadataFilePackageIndex"/>
|
||||||
<fileBasedIndex implementation="org.jetbrains.kotlin.idea.vfilefinder.KotlinModuleMappingIndex"/>
|
<fileBasedIndex implementation="org.jetbrains.kotlin.idea.vfilefinder.KotlinModuleMappingIndex"/>
|
||||||
<fileBasedIndex implementation="org.jetbrains.kotlin.idea.vfilefinder.KotlinPackageSourcesMemberNamesIndex"/>
|
<fileBasedIndex implementation="org.jetbrains.kotlin.idea.vfilefinder.KotlinPackageSourcesMemberNamesIndex"/>
|
||||||
|
<fileBasedIndex implementation="org.jetbrains.kotlin.idea.vfilefinder.KotlinJvmModuleAnnotationsIndex"/>
|
||||||
|
|
||||||
<idIndexer filetype="Kotlin" implementationClass="org.jetbrains.kotlin.idea.search.KotlinIdIndexer"/>
|
<idIndexer filetype="Kotlin" implementationClass="org.jetbrains.kotlin.idea.search.KotlinIdIndexer"/>
|
||||||
<todoIndexer filetype="Kotlin" implementationClass="org.jetbrains.kotlin.idea.search.KotlinTodoIndexer"/>
|
<todoIndexer filetype="Kotlin" implementationClass="org.jetbrains.kotlin.idea.search.KotlinTodoIndexer"/>
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package lib
|
||||||
|
|
||||||
|
@Experimental
|
||||||
|
annotation class ExperimentalAPI
|
||||||
|
|
||||||
|
class Foo
|
||||||
|
|
||||||
|
fun bar() {}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package usage
|
||||||
|
|
||||||
|
import lib.*
|
||||||
|
|
||||||
|
fun fail(foo: <error>Foo</error>): <error>Foo</error> {
|
||||||
|
<error>bar</error>()
|
||||||
|
return foo
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExperimentalAPI
|
||||||
|
fun ok(foo: Foo): Foo {
|
||||||
|
bar()
|
||||||
|
return foo
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package lib
|
||||||
|
|
||||||
|
@Experimental
|
||||||
|
annotation class ExperimentalAPI
|
||||||
|
|
||||||
|
class Foo
|
||||||
|
|
||||||
|
fun bar() {}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package usage
|
||||||
|
|
||||||
|
import lib.*
|
||||||
|
|
||||||
|
fun fail(foo: <error>Foo</error>): <error>Foo</error> {
|
||||||
|
<error>bar</error>()
|
||||||
|
return foo
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExperimentalAPI
|
||||||
|
fun ok(foo: Foo): Foo {
|
||||||
|
bar()
|
||||||
|
return foo
|
||||||
|
}
|
||||||
@@ -31,9 +31,11 @@ import org.jetbrains.kotlin.analyzer.ModuleInfo
|
|||||||
import org.jetbrains.kotlin.analyzer.ResolverForModuleComputationTracker
|
import org.jetbrains.kotlin.analyzer.ResolverForModuleComputationTracker
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
||||||
import org.jetbrains.kotlin.config.LanguageVersion
|
import org.jetbrains.kotlin.config.LanguageVersion
|
||||||
|
import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCommonCompilerArgumentsHolder
|
||||||
import org.jetbrains.kotlin.idea.completion.test.withServiceRegistered
|
import org.jetbrains.kotlin.idea.completion.test.withServiceRegistered
|
||||||
import org.jetbrains.kotlin.idea.facet.KotlinFacetConfiguration
|
import org.jetbrains.kotlin.idea.facet.KotlinFacetConfiguration
|
||||||
import org.jetbrains.kotlin.idea.facet.KotlinFacetType
|
import org.jetbrains.kotlin.idea.facet.KotlinFacetType
|
||||||
|
import org.jetbrains.kotlin.idea.framework.JSLibraryKind
|
||||||
import org.jetbrains.kotlin.idea.project.KotlinCodeBlockModificationListener
|
import org.jetbrains.kotlin.idea.project.KotlinCodeBlockModificationListener
|
||||||
import org.jetbrains.kotlin.idea.project.KotlinModuleModificationTracker
|
import org.jetbrains.kotlin.idea.project.KotlinModuleModificationTracker
|
||||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||||
@@ -42,6 +44,7 @@ import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
|||||||
import org.jetbrains.kotlin.idea.util.projectStructure.sdk
|
import org.jetbrains.kotlin.idea.util.projectStructure.sdk
|
||||||
import org.jetbrains.kotlin.samWithReceiver.SamWithReceiverCommandLineProcessor.Companion.ANNOTATION_OPTION
|
import org.jetbrains.kotlin.samWithReceiver.SamWithReceiverCommandLineProcessor.Companion.ANNOTATION_OPTION
|
||||||
import org.jetbrains.kotlin.samWithReceiver.SamWithReceiverCommandLineProcessor.Companion.PLUGIN_ID
|
import org.jetbrains.kotlin.samWithReceiver.SamWithReceiverCommandLineProcessor.Companion.PLUGIN_ID
|
||||||
|
import org.jetbrains.kotlin.test.MockLibraryUtil
|
||||||
import org.jetbrains.kotlin.test.TestJdkKind.FULL_JDK
|
import org.jetbrains.kotlin.test.TestJdkKind.FULL_JDK
|
||||||
|
|
||||||
open class MultiModuleHighlightingTest : AbstractMultiModuleHighlightingTest() {
|
open class MultiModuleHighlightingTest : AbstractMultiModuleHighlightingTest() {
|
||||||
@@ -209,6 +212,47 @@ open class MultiModuleHighlightingTest : AbstractMultiModuleHighlightingTest() {
|
|||||||
checkHighlightingInAllFiles()
|
checkHighlightingInAllFiles()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun testJvmExperimentalLibrary() {
|
||||||
|
val lib = MockLibraryUtil.compileJvmLibraryToJar(
|
||||||
|
testDataPath + "${getTestName(true)}/lib", "lib",
|
||||||
|
extraOptions = listOf(
|
||||||
|
"-Xskip-runtime-version-check",
|
||||||
|
"-language-version",
|
||||||
|
"1.3",
|
||||||
|
"-Xexperimental=lib.ExperimentalAPI"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
withSkipMetadataVersionCheck {
|
||||||
|
module("usage").addLibrary(lib)
|
||||||
|
checkHighlightingInAllFiles()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testJsExperimentalLibrary() {
|
||||||
|
val lib = MockLibraryUtil.compileJsLibraryToJar(
|
||||||
|
testDataPath + "${getTestName(true)}/lib", "lib", false,
|
||||||
|
extraOptions = listOf(
|
||||||
|
"-Xskip-runtime-version-check",
|
||||||
|
"-language-version",
|
||||||
|
"1.3",
|
||||||
|
"-Xexperimental=lib.ExperimentalAPI"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
withSkipMetadataVersionCheck {
|
||||||
|
module("usage").addLibrary(lib, kind = JSLibraryKind)
|
||||||
|
checkHighlightingInAllFiles()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun withSkipMetadataVersionCheck(block: () -> Unit) {
|
||||||
|
val holder = KotlinCommonCompilerArgumentsHolder.getInstance(project)
|
||||||
|
try {
|
||||||
|
holder.update { skipMetadataVersionCheck = true }
|
||||||
|
block()
|
||||||
|
} finally {
|
||||||
|
holder.update { skipMetadataVersionCheck = false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun Module.setupKotlinFacet(configure: KotlinFacetConfiguration.() -> Unit) = apply {
|
private fun Module.setupKotlinFacet(configure: KotlinFacetConfiguration.() -> Unit) = apply {
|
||||||
runWriteAction {
|
runWriteAction {
|
||||||
|
|||||||
Reference in New Issue
Block a user