[K/JS] Add ability to exclude declarations from export by a new annotation @JsExport.Ignore.

This commit is contained in:
Artem Kobzar
2022-10-03 11:07:25 +00:00
committed by Space Team
parent 917c8606f5
commit eb2326eabb
49 changed files with 970 additions and 42 deletions
@@ -89,13 +89,8 @@ public class PackageCodegenImpl implements PackageCodegen {
for (KtDeclaration declaration : file.getDeclarations()) {
if (declaration instanceof KtClassOrObject) {
ClassDescriptor descriptor = state.getBindingContext().get(BindingContext.CLASS, declaration);
if (PsiUtilsKt.hasExpectModifier(declaration)) {
if (descriptor != null && OptionalAnnotationUtil.shouldGenerateExpectClass(descriptor)) {
assert OptionalAnnotationUtil.isOptionalAnnotationClass(descriptor) :
"Expect class should be generated only if it's an optional annotation: " + descriptor;
state.getFactory().getPackagePartRegistry().getOptionalAnnotations().add(descriptor);
}
addDescriptorToOptionalAnnotationsIfNeeded((KtClassOrObject) declaration, state);
continue;
}
@@ -120,6 +115,27 @@ public class PackageCodegenImpl implements PackageCodegen {
}
}
private static void addDescriptorToOptionalAnnotationsIfNeeded(@NotNull KtClassOrObject declaration, @NotNull GenerationState state) {
ClassDescriptor descriptor = state.getBindingContext().get(BindingContext.CLASS, declaration);
if (descriptor == null || !OptionalAnnotationUtil.shouldGenerateExpectClass(descriptor)) {
return;
}
assert OptionalAnnotationUtil.isOptionalAnnotationClass(descriptor) :
"Expect class should be generated only if it's an optional annotation: " + descriptor;
state.getFactory().getPackagePartRegistry().getOptionalAnnotations().add(descriptor);
KtClassBody body = declaration.getBody();
if (body != null) {
for (KtDeclaration childDeclaration : body.getDeclarations()) {
if (!(childDeclaration instanceof KtClassOrObject)) continue;
addDescriptorToOptionalAnnotationsIfNeeded((KtClassOrObject) childDeclaration, state);
}
}
}
private void generateFile(@NotNull KtFile file) {
JvmFileClassInfo fileClassInfo = JvmFileClassUtil.getFileClassInfoNoResolve(file);
if (fileClassInfo.getWithJvmMultifileClass()) return;
@@ -789,7 +789,6 @@ public interface Errors {
DiagnosticFactory0<KtNamedDeclaration> ACTUAL_MISSING = DiagnosticFactory0.create(ERROR, ACTUAL_DECLARATION_NAME);
DiagnosticFactory0<PsiElement> OPTIONAL_EXPECTATION_NOT_ON_EXPECTED = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> NESTED_OPTIONAL_EXPECTATION = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE = DiagnosticFactory0.create(ERROR);
@@ -333,7 +333,6 @@ public class DefaultErrorMessages {
MAP.put(ACTUAL_MISSING, "Declaration must be marked with 'actual'");
MAP.put(OPTIONAL_EXPECTATION_NOT_ON_EXPECTED, "'@OptionalExpectation' can only be used on an expected annotation class");
MAP.put(NESTED_OPTIONAL_EXPECTATION, "'@OptionalExpectation' cannot be used on a nested class");
MAP.put(OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY, "Declaration annotated with '@OptionalExpectation' can only be used inside an annotation entry");
MAP.put(OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE, "Declaration annotated with '@OptionalExpectation' can only be used in common module sources");
@@ -7,25 +7,16 @@ package org.jetbrains.kotlin.resolve.checkers
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.MemberDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.multiplatform.OptionalAnnotationUtil
object OptionalExpectationChecker {
fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, trace: BindingTrace) {
val isExpect = descriptor is MemberDescriptor && descriptor.isExpect
if (isExpect) {
if (DescriptorUtils.isAnnotationClass(descriptor) && descriptor.containingDeclaration !is PackageFragmentDescriptor) {
getOptionalExpectationEntry(declaration, trace)?.let {
trace.report(Errors.NESTED_OPTIONAL_EXPECTATION.on(it))
}
}
} else {
if (descriptor !is MemberDescriptor || !descriptor.isExpect) {
getOptionalExpectationEntry(declaration, trace)?.let {
trace.report(Errors.OPTIONAL_EXPECTATION_NOT_ON_EXPECTED.on(it))
}
@@ -12,9 +12,11 @@ internal fun PsiElement.isUsageAsAnnotationOrImport(): Boolean {
val parent = parent
if (parent is KtUserType) {
return parent.parent is KtTypeReference &&
parent.parent.parent is KtConstructorCalleeExpression &&
parent.parent.parent.parent is KtAnnotationEntry
return (parent.parent is KtUserType && parent.isUsageAsAnnotationOrImport()) || (
parent.parent is KtTypeReference &&
parent.parent.parent is KtConstructorCalleeExpression &&
parent.parent.parent.parent is KtAnnotationEntry
)
}
return parent is KtDotQualifiedExpression && parent.parent is KtImportDirective
@@ -22,8 +22,6 @@ import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.serialization.js.ModuleKind
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.runIf
@@ -740,6 +738,9 @@ private fun shouldDeclarationBeExported(declaration: IrDeclarationWithName, cont
if (context.additionalExportedDeclarations.contains(declaration))
return true
if (declaration.isJsExportIgnore())
return false
if (declaration is IrOverridableDeclaration<*>) {
val overriddenNonEmpty = declaration
.overriddenSymbols
@@ -113,7 +113,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
if (property.getter?.extensionReceiverParameter != null || property.setter?.extensionReceiverParameter != null)
continue
if (!property.visibility.isPublicAPI || property.isSimpleProperty)
if (!property.visibility.isPublicAPI || property.isSimpleProperty || property.isJsExportIgnore())
continue
if (
@@ -223,6 +223,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
}
private fun IrSimpleFunction.isDefinedInsideExportedInterface(): Boolean {
if (isJsExportIgnore() || correspondingPropertySymbol?.owner?.isJsExportIgnore() == true) return false
return (!isFakeOverride && parentClassOrNull.isExportedInterface(context.staticContext.backendContext)) ||
overriddenSymbols.any { it.owner.isDefinedInsideExportedInterface() }
}
@@ -25,6 +25,7 @@ object JsAnnotations {
val jsQualifierFqn = FqName("kotlin.js.JsQualifier")
val jsExportFqn = FqName("kotlin.js.JsExport")
val jsImplicitExportFqn = FqName("kotlin.js.JsImplicitExport")
val jsExportIgnoreFqn = FqName("kotlin.js.JsExport.Ignore")
val jsNativeGetter = FqName("kotlin.js.nativeGetter")
val jsNativeSetter = FqName("kotlin.js.nativeSetter")
val jsNativeInvoke = FqName("kotlin.js.nativeInvoke")
@@ -61,6 +62,9 @@ fun IrAnnotationContainer.isJsExport(): Boolean =
fun IrAnnotationContainer.isJsImplicitExport(): Boolean =
hasAnnotation(JsAnnotations.jsImplicitExportFqn)
fun IrAnnotationContainer.isJsExportIgnore(): Boolean =
hasAnnotation(JsAnnotations.jsExportIgnoreFqn)
fun IrAnnotationContainer.isJsNativeGetter(): Boolean = hasAnnotation(JsAnnotations.jsNativeGetter)
fun IrAnnotationContainer.isJsNativeSetter(): Boolean = hasAnnotation(JsAnnotations.jsNativeSetter)
@@ -23,9 +23,17 @@ class ProcessOptionalAnnotations(private val context: JvmBackendContext) : FileL
override fun lower(irFile: IrFile) {
for (declaration in irFile.declarations) {
if (declaration !is IrClass || !declaration.isOptionalAnnotationClass) continue
// TODO FirMetadataSource.Class
val metadataSource = (declaration.metadata as? DescriptorMetadataSource.Class)?.descriptor ?: continue
context.state.factory.packagePartRegistry.optionalAnnotations += metadataSource
declaration.registerOptionalAnnotations()
}
}
private fun IrClass.registerOptionalAnnotations() {
// TODO FirMetadataSource.Class
val metadataSource = (metadata as? DescriptorMetadataSource.Class)?.descriptor ?: return
context.state.factory.packagePartRegistry.optionalAnnotations += metadataSource
declarations.forEach {
if (it is IrClass && it.isOptionalAnnotationClass) it.registerOptionalAnnotations()
}
}
}
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrClassPublicSymbolImpl
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
@@ -89,12 +90,18 @@ val IrClass.packageFqName: FqName?
get() = symbol.signature?.packageFqName() ?: parent.getPackageFragment()?.fqName
fun IrDeclarationWithName.hasEqualFqName(fqName: FqName): Boolean =
name == fqName.shortName() && when (val parent = parent) {
symbol.hasEqualFqName(fqName) || name == fqName.shortName() && when (val parent = parent) {
is IrPackageFragment -> parent.fqName == fqName.parent()
is IrDeclarationWithName -> parent.hasEqualFqName(fqName.parent())
else -> false
}
fun IrSymbol.hasEqualFqName(fqName: FqName): Boolean {
return this is IrClassPublicSymbolImpl && with(signature as? IdSignature.CommonSignature ?: return false) {
FqName("$packageFqName.$declarationFqName") == fqName
}
}
fun List<IrConstructorCall>.hasAnnotation(fqName: FqName): Boolean =
any { it.annotationClass.hasEqualFqName(fqName) }
@@ -13,7 +13,10 @@ package a
expect annotation class A(val x: Int)
@OptionalExpectation
expect annotation class B(val s: String)
expect annotation class B(val s: String) {
@OptionalExpectation
annotation class C(val a: Boolean)
}
// FILE: actual.kt
@@ -33,6 +36,7 @@ import a.B
@A(42)
@B("OK")
@B.C(true)
fun box(): String {
return "OK"
}
@@ -20,7 +20,6 @@ annotation class InOtherAnnotation(val a: A)
fun useInOtherAnnotation() {}
expect class C {
@OptionalExpectation
annotation class Nested
@@ -16,9 +16,6 @@ annotation class InOtherAnnotation(val a: A)
compiler/testData/multiplatform/optionalExpectationIncorrectUse/common.kt:19:20: error: declaration annotated with '@OptionalExpectation' can only be used inside an annotation entry
@InOtherAnnotation(A())
^
compiler/testData/multiplatform/optionalExpectationIncorrectUse/common.kt:25:5: error: '@OptionalExpectation' cannot be used on a nested class
@OptionalExpectation
^
-- JVM --
Exit code: COMPILATION_ERROR
@@ -41,9 +38,6 @@ annotation class InOtherAnnotation(val a: A)
compiler/testData/multiplatform/optionalExpectationIncorrectUse/common.kt:19:20: error: declaration annotated with '@OptionalExpectation' can only be used inside an annotation entry
@InOtherAnnotation(A())
^
compiler/testData/multiplatform/optionalExpectationIncorrectUse/common.kt:25:5: error: '@OptionalExpectation' cannot be used on a nested class
@OptionalExpectation
^
compiler/testData/multiplatform/optionalExpectationIncorrectUse/jvm.kt:1:24: error: declaration annotated with '@OptionalExpectation' can only be used inside an annotation entry
fun useInReturnType(): A? = null
^