Load module annotations in IDE
#KT-22759 Fixed
This commit is contained in:
@@ -126,12 +126,12 @@ object MockLibraryUtil {
|
||||
}
|
||||
|
||||
@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 outDir = File(contentDir, "out")
|
||||
val outputFile = File(outDir, jarName + ".js")
|
||||
compileKotlin2JS(sourcesPath, outputFile)
|
||||
compileKotlin2JS(sourcesPath, outputFile, extraOptions)
|
||||
|
||||
return createJarFile(contentDir, outDir, jarName, sourcesPath.takeIf { addSources })
|
||||
}
|
||||
@@ -189,8 +189,8 @@ object MockLibraryUtil {
|
||||
runJvmCompiler(args)
|
||||
}
|
||||
|
||||
private fun compileKotlin2JS(sourcesPath: String, outputFile: File) {
|
||||
runJsCompiler(listOf("-meta-info", "-output", outputFile.absolutePath, sourcesPath))
|
||||
private fun compileKotlin2JS(sourcesPath: String, outputFile: File, extraOptions: List<String>) {
|
||||
runJsCompiler(listOf("-meta-info", "-output", outputFile.absolutePath, sourcesPath, *extraOptions.toTypedArray()))
|
||||
}
|
||||
|
||||
fun compileKotlinModule(buildFilePath: String) {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.name;
|
||||
|
||||
import kotlin.text.StringsKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -94,6 +95,16 @@ public final class ClassId {
|
||||
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"
|
||||
*/
|
||||
|
||||
+4
-3
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.idea.caches.resolve
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.util.indexing.FileBasedIndex
|
||||
import org.jetbrains.kotlin.descriptors.PackagePartProvider
|
||||
import org.jetbrains.kotlin.idea.vfilefinder.KotlinJvmModuleAnnotationsIndex
|
||||
import org.jetbrains.kotlin.idea.vfilefinder.KotlinModuleMappingIndex
|
||||
import org.jetbrains.kotlin.load.kotlin.PackageParts
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
@@ -33,7 +34,7 @@ class IDEPackagePartProvider(val scope: GlobalSearchScope) : PackagePartProvider
|
||||
private fun getPackageParts(packageFqName: String): MutableList<PackageParts> =
|
||||
FileBasedIndex.getInstance().getValues(KotlinModuleMappingIndex.KEY, packageFqName, scope)
|
||||
|
||||
override fun getAnnotationsOnBinaryModule(moduleName: String): List<ClassId> {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
// Note that in case of several modules with the same name, we return all annotations on all of them, which is probably incorrect
|
||||
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)
|
||||
|
||||
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 read(input: DataInput) = IOUtil.readUTF(input)
|
||||
@@ -63,7 +63,7 @@ object KotlinModuleMappingIndex : FileBasedIndexExtension<String, PackageParts>(
|
||||
|
||||
override fun dependsOnFileContent() = true
|
||||
|
||||
override fun getKeyDescriptor() = KEY_DESCRIPTOR
|
||||
override fun getKeyDescriptor() = STRING_KEY_DESCRIPTOR
|
||||
|
||||
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();
|
||||
File libraryJar =
|
||||
isJsLibrary
|
||||
? MockLibraryUtil.compileJsLibraryToJar(sourcesPath, LIBRARY_NAME, withSources)
|
||||
? MockLibraryUtil.compileJsLibraryToJar(sourcesPath, LIBRARY_NAME, withSources, Collections.emptyList())
|
||||
: MockLibraryUtil.compileJvmLibraryToJar(sourcesPath, LIBRARY_NAME, withSources, true, extraOptions, classpath);
|
||||
String jarUrl = getJarUrl(libraryJar);
|
||||
|
||||
|
||||
@@ -247,6 +247,9 @@
|
||||
<projectService serviceInterface="org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver"
|
||||
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"
|
||||
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.KotlinModuleMappingIndex"/>
|
||||
<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"/>
|
||||
<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.cli.common.arguments.K2JVMCompilerArguments
|
||||
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.facet.KotlinFacetConfiguration
|
||||
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.KotlinModuleModificationTracker
|
||||
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.samWithReceiver.SamWithReceiverCommandLineProcessor.Companion.ANNOTATION_OPTION
|
||||
import org.jetbrains.kotlin.samWithReceiver.SamWithReceiverCommandLineProcessor.Companion.PLUGIN_ID
|
||||
import org.jetbrains.kotlin.test.MockLibraryUtil
|
||||
import org.jetbrains.kotlin.test.TestJdkKind.FULL_JDK
|
||||
|
||||
open class MultiModuleHighlightingTest : AbstractMultiModuleHighlightingTest() {
|
||||
@@ -209,6 +212,47 @@ open class MultiModuleHighlightingTest : AbstractMultiModuleHighlightingTest() {
|
||||
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 {
|
||||
runWriteAction {
|
||||
|
||||
Reference in New Issue
Block a user