Load module annotations for JVM and JS modules in compiler
#KT-22759 In Progress
This commit is contained in:
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.cli.jvm.compiler
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PackagePartProvider
|
||||
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 CliModuleAnnotationsResolver : ModuleAnnotationsResolver {
|
||||
private val packagePartProviders = mutableListOf<PackagePartProvider>()
|
||||
|
||||
fun addPackagePartProvider(packagePartProvider: PackagePartProvider) {
|
||||
packagePartProviders += packagePartProvider
|
||||
}
|
||||
|
||||
override fun getAnnotationsOnContainingModule(descriptor: DeclarationDescriptor): List<ClassId> {
|
||||
getAnnotationsOnContainingJsModule(descriptor)?.let { return it }
|
||||
|
||||
val moduleName = getJvmModuleNameForDeserializedDescriptor(descriptor) ?: return emptyList()
|
||||
return packagePartProviders.flatMap { it.getAnnotationsOnBinaryModule(moduleName) }
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.PackagePartProvider
|
||||
import org.jetbrains.kotlin.load.kotlin.ModuleMapping
|
||||
import org.jetbrains.kotlin.load.kotlin.PackageParts
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.resolve.CompilerDeserializationConfiguration
|
||||
import java.io.EOFException
|
||||
|
||||
@@ -31,7 +32,7 @@ class JvmPackagePartProvider(
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
private val scope: GlobalSearchScope
|
||||
) : PackagePartProvider {
|
||||
private data class ModuleMappingInfo(val root: VirtualFile, val mapping: ModuleMapping)
|
||||
private data class ModuleMappingInfo(val root: VirtualFile, val mapping: ModuleMapping, val name: String)
|
||||
|
||||
private val deserializationConfiguration = CompilerDeserializationConfiguration(languageVersionSettings)
|
||||
|
||||
@@ -68,6 +69,12 @@ class JvmPackagePartProvider(
|
||||
return result
|
||||
}
|
||||
|
||||
override fun getAnnotationsOnBinaryModule(moduleName: String): List<ClassId> {
|
||||
return loadedModules.mapNotNull { (_, mapping, name) ->
|
||||
if (name == moduleName) mapping.moduleData.annotations else null
|
||||
}.flatten()
|
||||
}
|
||||
|
||||
fun addRoots(roots: List<JavaRoot>) {
|
||||
for ((root, type) in roots) {
|
||||
if (type != JavaRoot.RootType.BINARY) continue
|
||||
@@ -83,7 +90,7 @@ class JvmPackagePartProvider(
|
||||
catch (e: EOFException) {
|
||||
throw RuntimeException("Error on reading package parts from $moduleFile in $root", e)
|
||||
}
|
||||
loadedModules.add(ModuleMappingInfo(root, mapping))
|
||||
loadedModules.add(ModuleMappingInfo(root, mapping, moduleFile.nameWithoutExtension))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,8 +29,6 @@ import com.intellij.ide.plugins.PluginManagerCore
|
||||
import com.intellij.lang.MetaLanguage
|
||||
import com.intellij.lang.java.JavaParserDefinition
|
||||
import com.intellij.lang.jvm.facade.JvmElementProvider
|
||||
import com.intellij.lang.jvm.facade.JvmFacade
|
||||
import com.intellij.lang.jvm.facade.JvmFacadeImpl
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.TransactionGuard
|
||||
import com.intellij.openapi.application.TransactionGuardImpl
|
||||
@@ -101,6 +99,7 @@ import org.jetbrains.kotlin.load.kotlin.VirtualFileFinderFactory
|
||||
import org.jetbrains.kotlin.parsing.KotlinParserDefinition
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.CodeAnalyzerInitializer
|
||||
import org.jetbrains.kotlin.resolve.ModuleAnnotationsResolver
|
||||
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
|
||||
import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade
|
||||
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
|
||||
@@ -277,6 +276,7 @@ class KotlinCoreEnvironment private constructor(
|
||||
return JvmPackagePartProvider(configuration.languageVersionSettings, scope).apply {
|
||||
addRoots(initialRoots)
|
||||
packagePartProviders += this
|
||||
(ModuleAnnotationsResolver.getInstance(project) as CliModuleAnnotationsResolver).addPackagePartProvider(this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -569,6 +569,7 @@ class KotlinCoreEnvironment private constructor(
|
||||
registerService(ScriptDependenciesProvider::class.java, CliScriptDependenciesProvider(projectEnvironment.project, scriptDefinitionProvider))
|
||||
registerService(KotlinJavaPsiFacade::class.java, KotlinJavaPsiFacade(this))
|
||||
registerService(KtLightClassForFacade.FacadeStubCache::class.java, KtLightClassForFacade.FacadeStubCache(this))
|
||||
registerService(ModuleAnnotationsResolver::class.java, CliModuleAnnotationsResolver())
|
||||
if (messageCollector != null) {
|
||||
registerService(ScriptReportSink::class.java, CliScriptReportSink(messageCollector))
|
||||
}
|
||||
|
||||
+5
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.load.kotlin.incremental
|
||||
import org.jetbrains.kotlin.descriptors.PackagePartProvider
|
||||
import org.jetbrains.kotlin.load.kotlin.ModuleMapping
|
||||
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
|
||||
@@ -42,4 +43,8 @@ class IncrementalPackagePartProvider(
|
||||
|
||||
// TODO
|
||||
override fun findMetadataPackageParts(packageFqName: String): List<String> = TODO()
|
||||
|
||||
override fun getAnnotationsOnBinaryModule(moduleName: String): List<ClassId> {
|
||||
return parent.getAnnotationsOnBinaryModule(moduleName)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.load.kotlin
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassOrPackageFragmentDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedMemberDescriptor
|
||||
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf
|
||||
|
||||
fun getJvmModuleNameForDeserializedDescriptor(descriptor: DeclarationDescriptor): String? {
|
||||
val parent = DescriptorUtils.getParentOfType(descriptor, ClassOrPackageFragmentDescriptor::class.java, false)
|
||||
|
||||
when {
|
||||
parent is DeserializedClassDescriptor -> {
|
||||
val classProto = parent.classProto
|
||||
val nameResolver = parent.c.nameResolver
|
||||
return classProto.getExtension(JvmProtoBuf.classModuleName)
|
||||
?.takeIf { classProto.hasExtension(JvmProtoBuf.classModuleName) }
|
||||
?.let(nameResolver::getString)
|
||||
?: JvmAbi.DEFAULT_MODULE_NAME
|
||||
}
|
||||
descriptor is DeserializedMemberDescriptor -> {
|
||||
val source = descriptor.containerSource
|
||||
if (source is JvmPackagePartSource) {
|
||||
val packageProto = source.packageProto
|
||||
val nameResolver = source.nameResolver
|
||||
return packageProto.getExtension(JvmProtoBuf.packageModuleName)
|
||||
?.takeIf { packageProto.hasExtension(JvmProtoBuf.packageModuleName) }
|
||||
?.let(nameResolver::getString)
|
||||
?: JvmAbi.DEFAULT_MODULE_NAME
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.*
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.KotlinResolutionStatelessCallbacksImpl
|
||||
import org.jetbrains.kotlin.resolve.checkers.ExperimentalUsageChecker
|
||||
import org.jetbrains.kotlin.resolve.lazy.*
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactory
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory
|
||||
@@ -63,6 +64,10 @@ fun StorageComponentContainer.configureModule(
|
||||
private fun StorageComponentContainer.configurePlatformIndependentComponents() {
|
||||
useImpl<SupertypeLoopCheckerImpl>()
|
||||
useImpl<KotlinResolutionStatelessCallbacksImpl>()
|
||||
|
||||
useImpl<ExperimentalUsageChecker>()
|
||||
useImpl<ExperimentalUsageChecker.Overrides>()
|
||||
useImpl<ExperimentalUsageChecker.ClassifierUsage>()
|
||||
}
|
||||
|
||||
fun StorageComponentContainer.configureModule(
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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.resolve
|
||||
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
|
||||
interface ModuleAnnotationsResolver {
|
||||
fun getAnnotationsOnContainingModule(descriptor: DeclarationDescriptor): List<ClassId>
|
||||
|
||||
companion object {
|
||||
fun getInstance(project: Project): ModuleAnnotationsResolver =
|
||||
ServiceManager.getService(project, ModuleAnnotationsResolver::class.java)
|
||||
}
|
||||
}
|
||||
@@ -81,8 +81,7 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf(
|
||||
DelegationChecker(),
|
||||
KClassWithIncorrectTypeArgumentChecker,
|
||||
SuspendOperatorsCheckers,
|
||||
InlineClassDeclarationChecker,
|
||||
ExperimentalUsageChecker.Overrides
|
||||
InlineClassDeclarationChecker
|
||||
)
|
||||
|
||||
private val DEFAULT_CALL_CHECKERS = listOf(
|
||||
@@ -92,12 +91,11 @@ private val DEFAULT_CALL_CHECKERS = listOf(
|
||||
CoroutineSuspendCallChecker, BuilderFunctionsCallChecker, DslScopeViolationCallChecker, MissingDependencyClassChecker,
|
||||
CallableReferenceCompatibilityChecker(), LateinitIntrinsicApplicabilityChecker,
|
||||
UnderscoreUsageChecker, AssigningNamedArgumentToVarargChecker(),
|
||||
PrimitiveNumericComparisonCallChecker, LambdaWithSuspendModifierCallChecker, ExperimentalUsageChecker
|
||||
PrimitiveNumericComparisonCallChecker, LambdaWithSuspendModifierCallChecker
|
||||
)
|
||||
private val DEFAULT_TYPE_CHECKERS = emptyList<AdditionalTypeChecker>()
|
||||
private val DEFAULT_CLASSIFIER_USAGE_CHECKERS = listOf(
|
||||
DeprecatedClassifierUsageChecker(), ApiVersionClassifierUsageChecker, MissingDependencyClassChecker.ClassifierUsage,
|
||||
ExperimentalUsageChecker.ClassifierUsage
|
||||
DeprecatedClassifierUsageChecker(), ApiVersionClassifierUsageChecker, MissingDependencyClassChecker.ClassifierUsage
|
||||
)
|
||||
private val DEFAULT_ANNOTATION_CHECKERS = listOf<AdditionalAnnotationChecker>(
|
||||
ExperimentalMarkerDeclarationAnnotationChecker
|
||||
|
||||
+220
-199
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.resolve.checkers
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.config.AnalysisFlag
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
@@ -26,8 +27,7 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.*
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
@@ -41,19 +41,8 @@ import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||
import org.jetbrains.kotlin.utils.SmartSet
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
object ExperimentalUsageChecker : CallChecker {
|
||||
internal val EXPERIMENTAL_FQ_NAME = FqName("kotlin.Experimental")
|
||||
internal val USE_EXPERIMENTAL_FQ_NAME = FqName("kotlin.UseExperimental")
|
||||
internal val USE_EXPERIMENTAL_ANNOTATION_CLASS = Name.identifier("markerClass")
|
||||
|
||||
private val LEVEL = Name.identifier("level")
|
||||
private val WARNING_LEVEL = Name.identifier("WARNING")
|
||||
private val ERROR_LEVEL = Name.identifier("ERROR")
|
||||
|
||||
internal val IMPACT = Name.identifier("changesMayBreak")
|
||||
private val COMPILATION_IMPACT = Name.identifier("COMPILATION")
|
||||
private val LINKAGE_IMPACT = Name.identifier("LINKAGE")
|
||||
private val RUNTIME_IMPACT = Name.identifier("RUNTIME")
|
||||
class ExperimentalUsageChecker(project: Project) : CallChecker {
|
||||
private val moduleAnnotationsResolver = ModuleAnnotationsResolver.getInstance(project)
|
||||
|
||||
internal data class Experimentality(
|
||||
val markerDescriptor: ClassDescriptor,
|
||||
@@ -73,235 +62,267 @@ object ExperimentalUsageChecker : CallChecker {
|
||||
}
|
||||
|
||||
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
|
||||
checkExperimental(resolvedCall.resultingDescriptor, reportOn, context)
|
||||
checkExperimental(resolvedCall.resultingDescriptor, reportOn, context, moduleAnnotationsResolver)
|
||||
}
|
||||
|
||||
private fun checkExperimental(descriptor: DeclarationDescriptor, element: PsiElement, context: CheckerContext) {
|
||||
val experimentalities = descriptor.loadExperimentalities()
|
||||
if (experimentalities.isNotEmpty()) {
|
||||
checkExperimental(
|
||||
experimentalities, element, context.trace.bindingContext, context.languageVersionSettings,
|
||||
context.moduleDescriptor
|
||||
) { experimentality, isBodyUsageOfSourceOnlyExperimentality ->
|
||||
val diagnostic = when (experimentality.severity) {
|
||||
Experimentality.Severity.WARNING -> Errors.EXPERIMENTAL_API_USAGE
|
||||
Experimentality.Severity.ERROR -> Errors.EXPERIMENTAL_API_USAGE_ERROR
|
||||
companion object {
|
||||
internal val EXPERIMENTAL_FQ_NAME = FqName("kotlin.Experimental")
|
||||
internal val USE_EXPERIMENTAL_FQ_NAME = FqName("kotlin.UseExperimental")
|
||||
internal val USE_EXPERIMENTAL_ANNOTATION_CLASS = Name.identifier("markerClass")
|
||||
|
||||
private val LEVEL = Name.identifier("level")
|
||||
private val WARNING_LEVEL = Name.identifier("WARNING")
|
||||
private val ERROR_LEVEL = Name.identifier("ERROR")
|
||||
|
||||
internal val IMPACT = Name.identifier("changesMayBreak")
|
||||
private val COMPILATION_IMPACT = Name.identifier("COMPILATION")
|
||||
private val LINKAGE_IMPACT = Name.identifier("LINKAGE")
|
||||
private val RUNTIME_IMPACT = Name.identifier("RUNTIME")
|
||||
|
||||
private fun checkExperimental(
|
||||
descriptor: DeclarationDescriptor,
|
||||
element: PsiElement,
|
||||
context: CheckerContext,
|
||||
moduleAnnotationsResolver: ModuleAnnotationsResolver
|
||||
) {
|
||||
val experimentalities = descriptor.loadExperimentalities(moduleAnnotationsResolver)
|
||||
if (experimentalities.isNotEmpty()) {
|
||||
checkExperimental(
|
||||
experimentalities, element, context.trace.bindingContext, context.languageVersionSettings,
|
||||
context.moduleDescriptor
|
||||
) { experimentality, isBodyUsageOfSourceOnlyExperimentality ->
|
||||
val diagnostic = when (experimentality.severity) {
|
||||
Experimentality.Severity.WARNING -> Errors.EXPERIMENTAL_API_USAGE
|
||||
Experimentality.Severity.ERROR -> Errors.EXPERIMENTAL_API_USAGE_ERROR
|
||||
}
|
||||
context.trace.report(diagnostic.on(element, experimentality.annotationFqName, isBodyUsageOfSourceOnlyExperimentality))
|
||||
}
|
||||
context.trace.report(diagnostic.on(element, experimentality.annotationFqName, isBodyUsageOfSourceOnlyExperimentality))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkExperimental(
|
||||
experimentalities: Collection<Experimentality>,
|
||||
element: PsiElement,
|
||||
bindingContext: BindingContext,
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
module: ModuleDescriptor,
|
||||
report: (experimentality: Experimentality, isBodyUsageOfCompilationExperimentality: Boolean) -> Unit
|
||||
) {
|
||||
val isBodyUsageExceptPublicInline = element.isBodyUsage(bindingContext, allowPublicInline = false)
|
||||
val isBodyUsage = isBodyUsageExceptPublicInline || element.isBodyUsage(bindingContext, allowPublicInline = true)
|
||||
private fun checkExperimental(
|
||||
experimentalities: Collection<Experimentality>,
|
||||
element: PsiElement,
|
||||
bindingContext: BindingContext,
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
module: ModuleDescriptor,
|
||||
report: (experimentality: Experimentality, isBodyUsageOfCompilationExperimentality: Boolean) -> Unit
|
||||
) {
|
||||
val isBodyUsageExceptPublicInline = element.isBodyUsage(bindingContext, allowPublicInline = false)
|
||||
val isBodyUsage = isBodyUsageExceptPublicInline || element.isBodyUsage(bindingContext, allowPublicInline = true)
|
||||
|
||||
for (experimentality in experimentalities) {
|
||||
val isBodyUsageOfCompilationExperimentality =
|
||||
experimentality.isCompilationOnly && isBodyUsage
|
||||
for (experimentality in experimentalities) {
|
||||
val isBodyUsageOfCompilationExperimentality =
|
||||
experimentality.isCompilationOnly && isBodyUsage
|
||||
|
||||
val isBodyUsageInSameModule =
|
||||
experimentality.markerDescriptor.module == module && isBodyUsageExceptPublicInline
|
||||
val isBodyUsageInSameModule =
|
||||
experimentality.markerDescriptor.module == module && isBodyUsageExceptPublicInline
|
||||
|
||||
val annotationFqName = experimentality.annotationFqName
|
||||
val isExperimentalityAccepted =
|
||||
isBodyUsageInSameModule ||
|
||||
(isBodyUsageOfCompilationExperimentality &&
|
||||
element.hasContainerAnnotatedWithUseExperimental(annotationFqName, bindingContext, languageVersionSettings)) ||
|
||||
element.propagates(annotationFqName, bindingContext, languageVersionSettings)
|
||||
val annotationFqName = experimentality.annotationFqName
|
||||
val isExperimentalityAccepted =
|
||||
isBodyUsageInSameModule ||
|
||||
(isBodyUsageOfCompilationExperimentality &&
|
||||
element.hasContainerAnnotatedWithUseExperimental(annotationFqName, bindingContext, languageVersionSettings)) ||
|
||||
element.propagates(annotationFqName, bindingContext, languageVersionSettings)
|
||||
|
||||
if (!isExperimentalityAccepted) {
|
||||
report(experimentality, isBodyUsageOfCompilationExperimentality)
|
||||
if (!isExperimentalityAccepted) {
|
||||
report(experimentality, isBodyUsageOfCompilationExperimentality)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun DeclarationDescriptor.loadExperimentalities(): Set<Experimentality> {
|
||||
val result = SmartSet.create<Experimentality>()
|
||||
private fun DeclarationDescriptor.loadExperimentalities(
|
||||
moduleAnnotationsResolver: ModuleAnnotationsResolver
|
||||
): Set<Experimentality> {
|
||||
val result = SmartSet.create<Experimentality>()
|
||||
|
||||
for (annotation in annotations) {
|
||||
result.addIfNotNull(annotation.annotationClass?.loadExperimentalityForMarkerAnnotation())
|
||||
}
|
||||
|
||||
val container = containingDeclaration
|
||||
if (container is ClassDescriptor && this !is ConstructorDescriptor) {
|
||||
for (annotation in container.annotations) {
|
||||
for (annotation in annotations) {
|
||||
result.addIfNotNull(annotation.annotationClass?.loadExperimentalityForMarkerAnnotation())
|
||||
}
|
||||
|
||||
val container = containingDeclaration
|
||||
if (container is ClassDescriptor && this !is ConstructorDescriptor) {
|
||||
for (annotation in container.annotations) {
|
||||
result.addIfNotNull(annotation.annotationClass?.loadExperimentalityForMarkerAnnotation())
|
||||
}
|
||||
}
|
||||
|
||||
for (moduleAnnotationClassId in moduleAnnotationsResolver.getAnnotationsOnContainingModule(this)) {
|
||||
val annotationClass = module.findClassAcrossModuleDependencies(moduleAnnotationClassId)
|
||||
result.addIfNotNull(annotationClass?.loadExperimentalityForMarkerAnnotation())
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
internal fun ClassDescriptor.loadExperimentalityForMarkerAnnotation(): Experimentality? {
|
||||
val experimental = annotations.findAnnotation(EXPERIMENTAL_FQ_NAME) ?: return null
|
||||
|
||||
internal fun ClassDescriptor.loadExperimentalityForMarkerAnnotation(): Experimentality? {
|
||||
val experimental = annotations.findAnnotation(EXPERIMENTAL_FQ_NAME) ?: return null
|
||||
val severity = when ((experimental.allValueArguments[LEVEL] as? EnumValue)?.enumEntryName) {
|
||||
WARNING_LEVEL -> Experimentality.Severity.WARNING
|
||||
ERROR_LEVEL -> Experimentality.Severity.ERROR
|
||||
else -> Experimentality.DEFAULT_SEVERITY
|
||||
}
|
||||
|
||||
val severity = when ((experimental.allValueArguments[LEVEL] as? EnumValue)?.enumEntryName) {
|
||||
WARNING_LEVEL -> Experimentality.Severity.WARNING
|
||||
ERROR_LEVEL -> Experimentality.Severity.ERROR
|
||||
else -> Experimentality.DEFAULT_SEVERITY
|
||||
val impact = (experimental.allValueArguments[IMPACT] as? ArrayValue)?.value?.mapNotNull { impact ->
|
||||
when ((impact as? EnumValue)?.enumEntryName) {
|
||||
COMPILATION_IMPACT -> Experimentality.Impact.COMPILATION
|
||||
LINKAGE_IMPACT, RUNTIME_IMPACT -> Experimentality.Impact.LINKAGE_OR_RUNTIME
|
||||
else -> null
|
||||
}
|
||||
} ?: Experimentality.DEFAULT_IMPACT
|
||||
|
||||
return Experimentality(this, fqNameSafe, severity, impact)
|
||||
}
|
||||
|
||||
val impact = (experimental.allValueArguments[IMPACT] as? ArrayValue)?.value?.mapNotNull { impact ->
|
||||
when ((impact as? EnumValue)?.enumEntryName) {
|
||||
COMPILATION_IMPACT -> Experimentality.Impact.COMPILATION
|
||||
LINKAGE_IMPACT, RUNTIME_IMPACT -> Experimentality.Impact.LINKAGE_OR_RUNTIME
|
||||
// Returns true if this element appears in the body of some function and is not visible in any non-local declaration signature.
|
||||
// If that's the case, one can opt-in to using the corresponding experimental API by annotating this element (or any of its
|
||||
// enclosing declarations) with @UseExperimental(X::class), not requiring propagation of the experimental annotation to the call sites.
|
||||
// (Note that this is allowed only if X's impact is [COMPILATION].)
|
||||
private fun PsiElement.isBodyUsage(bindingContext: BindingContext, allowPublicInline: Boolean): Boolean {
|
||||
return anyParentMatches { element, parent ->
|
||||
element == (parent as? KtDeclarationWithBody)?.bodyExpression?.takeIf {
|
||||
allowPublicInline || !parent.isPublicInline(bindingContext)
|
||||
} ||
|
||||
element == (parent as? KtDeclarationWithInitializer)?.initializer ||
|
||||
element == (parent as? KtClassInitializer)?.body ||
|
||||
element == (parent as? KtParameter)?.defaultValue ||
|
||||
element == (parent as? KtSuperTypeCallEntry)?.valueArgumentList ||
|
||||
element == (parent as? KtDelegatedSuperTypeEntry)?.delegateExpression ||
|
||||
element == (parent as? KtPropertyDelegate)?.expression
|
||||
}
|
||||
}
|
||||
|
||||
private fun PsiElement.isPublicInline(bindingContext: BindingContext): Boolean {
|
||||
val descriptor = when (this) {
|
||||
is KtFunction -> bindingContext.get(BindingContext.FUNCTION, this)
|
||||
is KtPropertyAccessor -> bindingContext.get(BindingContext.PROPERTY_ACCESSOR, this)
|
||||
else -> null
|
||||
}
|
||||
} ?: Experimentality.DEFAULT_IMPACT
|
||||
|
||||
return Experimentality(this, fqNameSafe, severity, impact)
|
||||
}
|
||||
|
||||
// Returns true if this element appears in the body of some function and is not visible in any non-local declaration signature.
|
||||
// If that's the case, one can opt-in to using the corresponding experimental API by annotating this element (or any of its
|
||||
// enclosing declarations) with @UseExperimental(X::class), not requiring propagation of the experimental annotation to the call sites.
|
||||
// (Note that this is allowed only if X's impact is [COMPILATION].)
|
||||
private fun PsiElement.isBodyUsage(bindingContext: BindingContext, allowPublicInline: Boolean): Boolean {
|
||||
return anyParentMatches { element, parent ->
|
||||
element == (parent as? KtDeclarationWithBody)?.bodyExpression?.takeIf {
|
||||
allowPublicInline || !parent.isPublicInline(bindingContext)
|
||||
} ||
|
||||
element == (parent as? KtDeclarationWithInitializer)?.initializer ||
|
||||
element == (parent as? KtClassInitializer)?.body ||
|
||||
element == (parent as? KtParameter)?.defaultValue ||
|
||||
element == (parent as? KtSuperTypeCallEntry)?.valueArgumentList ||
|
||||
element == (parent as? KtDelegatedSuperTypeEntry)?.delegateExpression ||
|
||||
element == (parent as? KtPropertyDelegate)?.expression
|
||||
return descriptor != null && descriptor.isInline && descriptor.effectiveVisibility().let {
|
||||
it == EffectiveVisibility.Public ||
|
||||
it == EffectiveVisibility.ProtectedBound ||
|
||||
it is EffectiveVisibility.Protected
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun PsiElement.isPublicInline(bindingContext: BindingContext): Boolean {
|
||||
val descriptor = when (this) {
|
||||
is KtFunction -> bindingContext.get(BindingContext.FUNCTION, this)
|
||||
is KtPropertyAccessor -> bindingContext.get(BindingContext.PROPERTY_ACCESSOR, this)
|
||||
else -> null
|
||||
}
|
||||
return descriptor != null && descriptor.isInline && descriptor.effectiveVisibility().let {
|
||||
it == EffectiveVisibility.Public ||
|
||||
it == EffectiveVisibility.ProtectedBound ||
|
||||
it is EffectiveVisibility.Protected
|
||||
}
|
||||
}
|
||||
|
||||
// Checks whether any of the non-local enclosing declarations is annotated with annotationFqName, effectively requiring
|
||||
// propagation for the experimental annotation to the call sites
|
||||
private fun PsiElement.propagates(
|
||||
annotationFqName: FqName,
|
||||
bindingContext: BindingContext,
|
||||
languageVersionSettings: LanguageVersionSettings
|
||||
): Boolean =
|
||||
annotationFqName.asString() in languageVersionSettings.getFlag(AnalysisFlag.experimental) ||
|
||||
anyParentMatches { element, _ ->
|
||||
if (element is KtDeclaration) {
|
||||
val descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element)
|
||||
descriptor != null && !DescriptorUtils.isLocal(descriptor) && descriptor.annotations.hasAnnotation(annotationFqName)
|
||||
} else false
|
||||
}
|
||||
|
||||
// Checks whether there's an element lexically above the tree, that is annotated with `@UseExperimental(X::class)`
|
||||
// where annotationFqName is the FQ name of X
|
||||
private fun PsiElement.hasContainerAnnotatedWithUseExperimental(
|
||||
annotationFqName: FqName,
|
||||
bindingContext: BindingContext,
|
||||
languageVersionSettings: LanguageVersionSettings
|
||||
): Boolean =
|
||||
annotationFqName.asString() in languageVersionSettings.getFlag(AnalysisFlag.useExperimental) ||
|
||||
anyParentMatches { element, _ ->
|
||||
element is KtAnnotated && element.annotationEntries.any { entry ->
|
||||
bindingContext.get(BindingContext.ANNOTATION, entry)?.isUseExperimental(annotationFqName) == true
|
||||
// Checks whether any of the non-local enclosing declarations is annotated with annotationFqName, effectively requiring
|
||||
// propagation for the experimental annotation to the call sites
|
||||
private fun PsiElement.propagates(
|
||||
annotationFqName: FqName,
|
||||
bindingContext: BindingContext,
|
||||
languageVersionSettings: LanguageVersionSettings
|
||||
): Boolean =
|
||||
annotationFqName.asString() in languageVersionSettings.getFlag(AnalysisFlag.experimental) ||
|
||||
anyParentMatches { element, _ ->
|
||||
if (element is KtDeclaration) {
|
||||
val descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element)
|
||||
descriptor != null && !DescriptorUtils.isLocal(descriptor) &&
|
||||
descriptor.annotations.hasAnnotation(annotationFqName)
|
||||
} else false
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun PsiElement.anyParentMatches(predicate: (element: PsiElement, parent: PsiElement?) -> Boolean): Boolean {
|
||||
var element = this
|
||||
while (true) {
|
||||
val parent = element.parent
|
||||
if (predicate(element, parent)) return true
|
||||
element = parent ?: return false
|
||||
}
|
||||
}
|
||||
|
||||
private fun AnnotationDescriptor.isUseExperimental(annotationFqName: FqName): Boolean {
|
||||
if (fqName != USE_EXPERIMENTAL_FQ_NAME) return false
|
||||
|
||||
val annotationClasses = allValueArguments[USE_EXPERIMENTAL_ANNOTATION_CLASS]
|
||||
return annotationClasses is ArrayValue && annotationClasses.value.any { annotationClass ->
|
||||
(annotationClass as? KClassValue)?.value?.constructor?.declarationDescriptor?.fqNameSafe == annotationFqName
|
||||
}
|
||||
}
|
||||
|
||||
fun checkCompilerArguments(
|
||||
module: ModuleDescriptor,
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
reportError: (String) -> Unit,
|
||||
reportWarning: (String) -> Unit
|
||||
) {
|
||||
// Ideally, we should run full resolution (with all classifier usage checkers) on classifiers used in "-Xexperimental" and
|
||||
// "-Xuse-experimental" arguments. However, it's not easy to do this. This should be solved in the future with the support of
|
||||
// module annotations. For now, we only check deprecations because this is needed to correctly retire unneeded compiler arguments.
|
||||
val deprecationResolver = DeprecationResolver(LockBasedStorageManager(), languageVersionSettings)
|
||||
|
||||
fun checkAnnotation(fqName: String, allowNonCompilationImpact: Boolean): Boolean {
|
||||
val descriptor = module.resolveClassByFqName(FqName(fqName), NoLookupLocation.FOR_NON_TRACKED_SCOPE)
|
||||
val experimentality = descriptor?.loadExperimentalityForMarkerAnnotation()
|
||||
val message = when {
|
||||
descriptor == null ->
|
||||
"Experimental API marker $fqName is unresolved. " +
|
||||
"Please make sure it's present in the module dependencies"
|
||||
experimentality == null ->
|
||||
"Class $fqName is not an experimental API marker annotation"
|
||||
!allowNonCompilationImpact && !experimentality.impact.all(Experimentality.Impact.COMPILATION::equals) ->
|
||||
"Experimental API marker $fqName has impact other than COMPILATION, " +
|
||||
"therefore it can't be used with -Xuse-experimental"
|
||||
else -> {
|
||||
for (deprecation in deprecationResolver.getDeprecations(descriptor)) {
|
||||
val report = when (deprecation.deprecationLevel) {
|
||||
DeprecationLevelValue.WARNING -> reportWarning
|
||||
DeprecationLevelValue.ERROR, DeprecationLevelValue.HIDDEN -> reportError
|
||||
// Checks whether there's an element lexically above the tree, that is annotated with `@UseExperimental(X::class)`
|
||||
// where annotationFqName is the FQ name of X
|
||||
private fun PsiElement.hasContainerAnnotatedWithUseExperimental(
|
||||
annotationFqName: FqName,
|
||||
bindingContext: BindingContext,
|
||||
languageVersionSettings: LanguageVersionSettings
|
||||
): Boolean =
|
||||
annotationFqName.asString() in languageVersionSettings.getFlag(AnalysisFlag.useExperimental) ||
|
||||
anyParentMatches { element, _ ->
|
||||
element is KtAnnotated && element.annotationEntries.any { entry ->
|
||||
bindingContext.get(BindingContext.ANNOTATION, entry)?.isUseExperimental(annotationFqName) == true
|
||||
}
|
||||
report("Experimental API marker $fqName is deprecated" + deprecation.message?.let { ". $it" }.orEmpty())
|
||||
}
|
||||
return true
|
||||
|
||||
private inline fun PsiElement.anyParentMatches(predicate: (element: PsiElement, parent: PsiElement?) -> Boolean): Boolean {
|
||||
var element = this
|
||||
while (true) {
|
||||
val parent = element.parent
|
||||
if (predicate(element, parent)) return true
|
||||
element = parent ?: return false
|
||||
}
|
||||
}
|
||||
|
||||
private fun AnnotationDescriptor.isUseExperimental(annotationFqName: FqName): Boolean {
|
||||
if (fqName != USE_EXPERIMENTAL_FQ_NAME) return false
|
||||
|
||||
val annotationClasses = allValueArguments[USE_EXPERIMENTAL_ANNOTATION_CLASS]
|
||||
return annotationClasses is ArrayValue && annotationClasses.value.any { annotationClass ->
|
||||
(annotationClass as? KClassValue)?.value?.constructor?.declarationDescriptor?.fqNameSafe == annotationFqName
|
||||
}
|
||||
}
|
||||
|
||||
fun checkCompilerArguments(
|
||||
module: ModuleDescriptor,
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
reportError: (String) -> Unit,
|
||||
reportWarning: (String) -> Unit
|
||||
) {
|
||||
// Ideally, we should run full resolution (with all classifier usage checkers) on classifiers used in "-Xexperimental" and
|
||||
// "-Xuse-experimental" arguments. However, it's not easy to do this. This should be solved in the future with the support of
|
||||
// module annotations. For now, we only check deprecations because this is needed to correctly retire unneeded compiler arguments.
|
||||
val deprecationResolver = DeprecationResolver(LockBasedStorageManager(), languageVersionSettings)
|
||||
|
||||
fun checkAnnotation(fqName: String, allowNonCompilationImpact: Boolean): Boolean {
|
||||
val descriptor = module.resolveClassByFqName(FqName(fqName), NoLookupLocation.FOR_NON_TRACKED_SCOPE)
|
||||
val experimentality = descriptor?.loadExperimentalityForMarkerAnnotation()
|
||||
val message = when {
|
||||
descriptor == null ->
|
||||
"Experimental API marker $fqName is unresolved. " +
|
||||
"Please make sure it's present in the module dependencies"
|
||||
experimentality == null ->
|
||||
"Class $fqName is not an experimental API marker annotation"
|
||||
!allowNonCompilationImpact && !experimentality.impact.all(Experimentality.Impact.COMPILATION::equals) ->
|
||||
"Experimental API marker $fqName has impact other than COMPILATION, " +
|
||||
"therefore it can't be used with -Xuse-experimental"
|
||||
else -> {
|
||||
for (deprecation in deprecationResolver.getDeprecations(descriptor)) {
|
||||
val report = when (deprecation.deprecationLevel) {
|
||||
DeprecationLevelValue.WARNING -> reportWarning
|
||||
DeprecationLevelValue.ERROR, DeprecationLevelValue.HIDDEN -> reportError
|
||||
}
|
||||
report("Experimental API marker $fqName is deprecated" + deprecation.message?.let { ". $it" }.orEmpty())
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
reportError(message)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
reportError(message)
|
||||
val validExperimental =
|
||||
languageVersionSettings.getFlag(AnalysisFlag.experimental)
|
||||
.filter { checkAnnotation(it, allowNonCompilationImpact = true) }
|
||||
val validUseExperimental =
|
||||
languageVersionSettings.getFlag(AnalysisFlag.useExperimental)
|
||||
.filter { checkAnnotation(it, allowNonCompilationImpact = false) }
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
val validExperimental =
|
||||
languageVersionSettings.getFlag(AnalysisFlag.experimental)
|
||||
.filter { checkAnnotation(it, allowNonCompilationImpact = true) }
|
||||
val validUseExperimental =
|
||||
languageVersionSettings.getFlag(AnalysisFlag.useExperimental)
|
||||
.filter { checkAnnotation(it, allowNonCompilationImpact = false) }
|
||||
|
||||
for (fqName in validExperimental.intersect(validUseExperimental)) {
|
||||
reportError("'-Xuse-experimental=$fqName' has no effect because '-Xexperimental=$fqName' is used")
|
||||
for (fqName in validExperimental.intersect(validUseExperimental)) {
|
||||
reportError("'-Xuse-experimental=$fqName' has no effect because '-Xexperimental=$fqName' is used")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object ClassifierUsage : ClassifierUsageChecker {
|
||||
class ClassifierUsage(project: Project) : ClassifierUsageChecker {
|
||||
private val moduleAnnotationsResolver = ModuleAnnotationsResolver.getInstance(project)
|
||||
|
||||
override fun check(targetDescriptor: ClassifierDescriptor, element: PsiElement, context: ClassifierUsageCheckerContext) {
|
||||
checkExperimental(targetDescriptor, element, context)
|
||||
checkExperimental(targetDescriptor, element, context, moduleAnnotationsResolver)
|
||||
}
|
||||
}
|
||||
|
||||
object Overrides : DeclarationChecker {
|
||||
class Overrides(project: Project) : DeclarationChecker {
|
||||
private val moduleAnnotationsResolver = ModuleAnnotationsResolver.getInstance(project)
|
||||
|
||||
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
|
||||
if (descriptor !is CallableMemberDescriptor) return
|
||||
|
||||
val experimentalOverridden = descriptor.overriddenDescriptors.flatMap { member ->
|
||||
member.loadExperimentalities().map { experimentality -> experimentality to member }
|
||||
member.loadExperimentalities(moduleAnnotationsResolver).map { experimentality -> experimentality to member }
|
||||
}.toMap()
|
||||
|
||||
val module = descriptor.module
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package lib
|
||||
|
||||
@Experimental
|
||||
annotation class ExperimentalAPI
|
||||
|
||||
class Foo
|
||||
|
||||
fun bar() {}
|
||||
@@ -0,0 +1,7 @@
|
||||
compiler/testData/experimental/jsExperimentalModule/usage.kt:5:15: error: this declaration is experimental and its usage must be marked with '@lib.ExperimentalAPI'
|
||||
fun fail(foo: Foo) {
|
||||
^
|
||||
compiler/testData/experimental/jsExperimentalModule/usage.kt:6:5: error: this declaration is experimental and its usage must be marked with '@lib.ExperimentalAPI'
|
||||
bar()
|
||||
^
|
||||
COMPILATION_ERROR
|
||||
@@ -0,0 +1,12 @@
|
||||
package usage
|
||||
|
||||
import lib.*
|
||||
|
||||
fun fail(foo: Foo) {
|
||||
bar()
|
||||
}
|
||||
|
||||
@ExperimentalAPI
|
||||
fun ok(foo: Foo) {
|
||||
bar()
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package lib
|
||||
|
||||
@Experimental
|
||||
annotation class ExperimentalAPI
|
||||
|
||||
class Foo
|
||||
|
||||
fun bar() {}
|
||||
@@ -0,0 +1,7 @@
|
||||
compiler/testData/experimental/jvmExperimentalModule/usage.kt:5:15: error: this declaration is experimental and its usage must be marked with '@lib.ExperimentalAPI'
|
||||
fun fail(foo: Foo) {
|
||||
^
|
||||
compiler/testData/experimental/jvmExperimentalModule/usage.kt:6:5: error: this declaration is experimental and its usage must be marked with '@lib.ExperimentalAPI'
|
||||
bar()
|
||||
^
|
||||
COMPILATION_ERROR
|
||||
@@ -0,0 +1,12 @@
|
||||
package usage
|
||||
|
||||
import lib.*
|
||||
|
||||
fun fail(foo: Foo) {
|
||||
bar()
|
||||
}
|
||||
|
||||
@ExperimentalAPI
|
||||
fun ok(foo: Foo) {
|
||||
bar()
|
||||
}
|
||||
+9
-3
@@ -99,10 +99,16 @@ abstract class AbstractKotlinCompilerIntegrationTest : TestCaseWithTmpdir() {
|
||||
*
|
||||
* @return the path to the corresponding .meta.js file, i.e. "[libraryName].meta.js"
|
||||
*/
|
||||
protected fun compileJsLibrary(libraryName: String): File {
|
||||
protected fun compileJsLibrary(
|
||||
libraryName: String,
|
||||
additionalOptions: List<String> = emptyList(),
|
||||
checkKotlinOutput: (String) -> Unit = { actual -> assertEquals(normalizeOutput("" to ExitCode.OK), actual) }
|
||||
): File {
|
||||
val destination = File(tmpdir, "$libraryName.js")
|
||||
val output = compileKotlin(libraryName, destination, compiler = K2JSCompiler(), expectedFileName = null)
|
||||
assertEquals(normalizeOutput("" to ExitCode.OK), normalizeOutput(output))
|
||||
val output = compileKotlin(
|
||||
libraryName, destination, compiler = K2JSCompiler(), additionalOptions = additionalOptions, expectedFileName = null
|
||||
)
|
||||
checkKotlinOutput(normalizeOutput(output))
|
||||
return File(tmpdir, "$libraryName.meta.js")
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.jvm.compiler
|
||||
|
||||
import org.jetbrains.kotlin.cli.js.K2JSCompiler
|
||||
import java.io.File
|
||||
|
||||
class ExperimentalIntegrationTest : AbstractKotlinCompilerIntegrationTest() {
|
||||
override val testDataPath: String
|
||||
get() = "compiler/testData/experimental/"
|
||||
|
||||
fun testJvmExperimentalModule() {
|
||||
val lib = compileLibrary(
|
||||
"lib", additionalOptions = listOf(
|
||||
"-Xskip-runtime-version-check",
|
||||
"-language-version",
|
||||
"1.3",
|
||||
"-Xexperimental=lib.ExperimentalAPI"
|
||||
),
|
||||
checkKotlinOutput = { output -> output.trimEnd().endsWith("OK") }
|
||||
)
|
||||
compileKotlin("usage.kt", tmpdir, listOf(lib), additionalOptions = listOf("-Xskip-metadata-version-check"))
|
||||
}
|
||||
|
||||
fun testJsExperimentalModule() {
|
||||
val lib = compileJsLibrary(
|
||||
"lib", additionalOptions = listOf(
|
||||
"-Xskip-runtime-version-check",
|
||||
"-language-version",
|
||||
"1.3",
|
||||
"-Xexperimental=lib.ExperimentalAPI"
|
||||
),
|
||||
checkKotlinOutput = { output -> output.trimEnd().endsWith("OK") }
|
||||
)
|
||||
compileKotlin(
|
||||
"usage.kt", File(tmpdir, "usage.js"), listOf(lib), K2JSCompiler(),
|
||||
additionalOptions = listOf("-Xskip-metadata-version-check")
|
||||
)
|
||||
}
|
||||
}
|
||||
+6
@@ -18,6 +18,7 @@ package kotlin.reflect.jvm.internal.components
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.PackagePartProvider
|
||||
import org.jetbrains.kotlin.load.kotlin.ModuleMapping
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
@@ -44,4 +45,9 @@ class RuntimePackagePartProvider(private val classLoader: ClassLoader) : Package
|
||||
|
||||
// TODO
|
||||
override fun findMetadataPackageParts(packageFqName: String): List<String> = TODO()
|
||||
|
||||
override fun getAnnotationsOnBinaryModule(moduleName: String): List<ClassId> {
|
||||
// TODO: load annotations from resource files
|
||||
return emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.jetbrains.kotlin.descriptors
|
||||
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
|
||||
interface PackagePartProvider {
|
||||
/**
|
||||
* @return JVM internal names of package parts existing in the package with the given FQ name.
|
||||
@@ -31,9 +33,13 @@ interface PackagePartProvider {
|
||||
*/
|
||||
fun findMetadataPackageParts(packageFqName: String): List<String>
|
||||
|
||||
fun getAnnotationsOnBinaryModule(moduleName: String): List<ClassId>
|
||||
|
||||
object Empty : PackagePartProvider {
|
||||
override fun findPackageParts(packageFqName: String): List<String> = emptyList()
|
||||
|
||||
override fun findMetadataPackageParts(packageFqName: String): List<String> = emptyList()
|
||||
|
||||
override fun getAnnotationsOnBinaryModule(moduleName: String): List<ClassId> = emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -21,6 +21,7 @@ import com.intellij.util.indexing.FileBasedIndex
|
||||
import org.jetbrains.kotlin.descriptors.PackagePartProvider
|
||||
import org.jetbrains.kotlin.idea.vfilefinder.KotlinModuleMappingIndex
|
||||
import org.jetbrains.kotlin.load.kotlin.PackageParts
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
|
||||
class IDEPackagePartProvider(val scope: GlobalSearchScope) : PackagePartProvider {
|
||||
override fun findPackageParts(packageFqName: String): List<String> =
|
||||
@@ -31,4 +32,8 @@ 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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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.js.resolve
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassOrPackageFragmentDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedMemberDescriptor
|
||||
import org.jetbrains.kotlin.serialization.js.KotlinJavascriptPackageFragment
|
||||
|
||||
fun getAnnotationsOnContainingJsModule(descriptor: DeclarationDescriptor): List<ClassId>? {
|
||||
val parent = DescriptorUtils.getParentOfType(descriptor, ClassOrPackageFragmentDescriptor::class.java, false) ?: return emptyList()
|
||||
|
||||
val parentSource = (descriptor as? DeserializedMemberDescriptor)?.containerSource ?: parent.source
|
||||
return (parentSource as? KotlinJavascriptPackageFragment.JsContainerSource)?.annotations
|
||||
}
|
||||
Reference in New Issue
Block a user