[K/JS] Add @deprecated commentary if @Deprecated annotation exists on a declaration.
^KT-56405 Fixed
This commit is contained in:
@@ -10,7 +10,13 @@ import org.jetbrains.kotlin.ir.declarations.IrFunction
|
|||||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||||
|
|
||||||
sealed class ExportedDeclaration
|
sealed class ExportedDeclaration {
|
||||||
|
val attributes = mutableListOf<ExportedAttribute>()
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class ExportedAttribute {
|
||||||
|
class DeprecatedAttribute(val message: String): ExportedAttribute()
|
||||||
|
}
|
||||||
|
|
||||||
data class ExportedModule(
|
data class ExportedModule(
|
||||||
val name: String,
|
val name: String,
|
||||||
|
|||||||
+12
-5
@@ -6,6 +6,7 @@
|
|||||||
package org.jetbrains.kotlin.ir.backend.js.export
|
package org.jetbrains.kotlin.ir.backend.js.export
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.ir.isExpect
|
import org.jetbrains.kotlin.backend.common.ir.isExpect
|
||||||
|
import org.jetbrains.kotlin.builtins.StandardNames
|
||||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||||
@@ -61,7 +62,7 @@ class ExportModelGenerator(val context: JsIrBackendContext, val generateNamespac
|
|||||||
is IrClass -> exportClass(candidate)
|
is IrClass -> exportClass(candidate)
|
||||||
is IrField -> null
|
is IrField -> null
|
||||||
else -> error("Can't export declaration $candidate")
|
else -> error("Can't export declaration $candidate")
|
||||||
}
|
}?.withAttributesFor(candidate)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun exportClass(candidate: IrClass): ExportedDeclaration? {
|
private fun exportClass(candidate: IrClass): ExportedDeclaration? {
|
||||||
@@ -315,16 +316,16 @@ class ExportModelGenerator(val context: JsIrBackendContext, val generateNamespac
|
|||||||
|
|
||||||
when (candidate) {
|
when (candidate) {
|
||||||
is IrSimpleFunction ->
|
is IrSimpleFunction ->
|
||||||
members.addIfNotNull(exportFunction(candidate))
|
members.addIfNotNull(exportFunction(candidate)?.withAttributesFor(candidate))
|
||||||
|
|
||||||
is IrConstructor ->
|
is IrConstructor ->
|
||||||
members.addIfNotNull(exportConstructor(candidate))
|
members.addIfNotNull(exportConstructor(candidate)?.withAttributesFor(candidate))
|
||||||
|
|
||||||
is IrProperty ->
|
is IrProperty ->
|
||||||
members.addIfNotNull(exportProperty(candidate))
|
members.addIfNotNull(exportProperty(candidate)?.withAttributesFor(candidate))
|
||||||
|
|
||||||
is IrClass -> {
|
is IrClass -> {
|
||||||
val ec = exportClass(candidate)
|
val ec = exportClass(candidate)?.withAttributesFor(candidate)
|
||||||
if (ec is ExportedClass) {
|
if (ec is ExportedClass) {
|
||||||
nestedClasses.add(ec)
|
nestedClasses.add(ec)
|
||||||
} else {
|
} else {
|
||||||
@@ -549,6 +550,12 @@ class ExportModelGenerator(val context: JsIrBackendContext, val generateNamespac
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun ExportedDeclaration.withAttributesFor(declaration: IrDeclaration): ExportedDeclaration {
|
||||||
|
declaration.getDeprecated()?.let { attributes.add(ExportedAttribute.DeprecatedAttribute(it)) }
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
private fun exportType(type: IrType, shouldCalculateExportedSupertypeForImplicit: Boolean = true): ExportedType {
|
private fun exportType(type: IrType, shouldCalculateExportedSupertypeForImplicit: Boolean = true): ExportedType {
|
||||||
if (type is IrDynamicType)
|
if (type is IrDynamicType)
|
||||||
return ExportedType.Primitive.Any
|
return ExportedType.Primitive.Any
|
||||||
|
|||||||
+16
-1
@@ -96,7 +96,7 @@ class ExportModelToTsDeclarations {
|
|||||||
joinToString("") { it.toTypeScript(indent) + "\n" }
|
joinToString("") { it.toTypeScript(indent) + "\n" }
|
||||||
|
|
||||||
private fun ExportedDeclaration.toTypeScript(indent: String, prefix: String = "", esModules: Boolean = false): String =
|
private fun ExportedDeclaration.toTypeScript(indent: String, prefix: String = "", esModules: Boolean = false): String =
|
||||||
indent + when (this) {
|
attributes.toTypeScript(indent) + indent + when (this) {
|
||||||
is ErrorDeclaration -> generateTypeScriptString()
|
is ErrorDeclaration -> generateTypeScriptString()
|
||||||
is ExportedConstructor -> generateTypeScriptString(indent)
|
is ExportedConstructor -> generateTypeScriptString(indent)
|
||||||
is ExportedConstructSignature -> generateTypeScriptString(indent)
|
is ExportedConstructSignature -> generateTypeScriptString(indent)
|
||||||
@@ -107,6 +107,17 @@ class ExportModelToTsDeclarations {
|
|||||||
is ExportedObject -> generateTypeScriptString(indent, prefix, esModules)
|
is ExportedObject -> generateTypeScriptString(indent, prefix, esModules)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun Iterable<ExportedAttribute>.toTypeScript(indent: String): String {
|
||||||
|
return joinToString("\n") { it.toTypeScript(indent) }
|
||||||
|
.run { if (isNotEmpty()) plus("\n") else this }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ExportedAttribute.toTypeScript(indent: String): String {
|
||||||
|
return when (this) {
|
||||||
|
is ExportedAttribute.DeprecatedAttribute -> indent + tsDeprecated(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun ErrorDeclaration.generateTypeScriptString(): String {
|
private fun ErrorDeclaration.generateTypeScriptString(): String {
|
||||||
return "/* ErrorDeclaration: $message */"
|
return "/* ErrorDeclaration: $message */"
|
||||||
}
|
}
|
||||||
@@ -479,4 +490,8 @@ class ExportModelToTsDeclarations {
|
|||||||
private fun tsIgnore(reason: String): String {
|
private fun tsIgnore(reason: String): String {
|
||||||
return "/* @ts-ignore: $reason */"
|
return "/* @ts-ignore: $reason */"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun tsDeprecated(message: String): String {
|
||||||
|
return "/** @deprecated $message */"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.ir.backend.js.utils
|
package org.jetbrains.kotlin.ir.backend.js.utils
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.builtins.StandardNames
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer
|
import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
|
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
|
||||||
@@ -48,6 +49,9 @@ fun IrAnnotationContainer.getJsQualifier(): String? =
|
|||||||
fun IrAnnotationContainer.getJsName(): String? =
|
fun IrAnnotationContainer.getJsName(): String? =
|
||||||
getAnnotation(JsAnnotations.jsNameFqn)?.getSingleConstStringArgument()
|
getAnnotation(JsAnnotations.jsNameFqn)?.getSingleConstStringArgument()
|
||||||
|
|
||||||
|
fun IrAnnotationContainer.getDeprecated(): String? =
|
||||||
|
getAnnotation(StandardNames.FqNames.deprecated)?.getSingleConstStringArgument()
|
||||||
|
|
||||||
fun IrAnnotationContainer.hasJsPolyfill(): Boolean =
|
fun IrAnnotationContainer.hasJsPolyfill(): Boolean =
|
||||||
hasAnnotation(JsAnnotations.JsPolyfillFqn)
|
hasAnnotation(JsAnnotations.JsPolyfillFqn)
|
||||||
|
|
||||||
|
|||||||
Generated
+32
@@ -121,6 +121,38 @@ public class IrJsTypeScriptExportTestGenerated extends AbstractIrJsTypeScriptExp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@TestMetadata("js/js.translator/testData/typescript-export/deprecated")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
public class Deprecated {
|
||||||
|
@Test
|
||||||
|
public void testAllFilesPresentInDeprecated() throws Exception {
|
||||||
|
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/typescript-export/deprecated"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("deprecated.kt")
|
||||||
|
public void testDeprecated() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/typescript-export/deprecated/deprecated.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@TestMetadata("js/js.translator/testData/typescript-export/deprecated-in-exported-file")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
public class Deprecated_in_exported_file {
|
||||||
|
@Test
|
||||||
|
public void testAllFilesPresentInDeprecated_in_exported_file() throws Exception {
|
||||||
|
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/typescript-export/deprecated-in-exported-file"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("deprecated.kt")
|
||||||
|
public void testDeprecated() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/typescript-export/deprecated-in-exported-file/deprecated.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
@TestMetadata("js/js.translator/testData/typescript-export/enum-classes")
|
@TestMetadata("js/js.translator/testData/typescript-export/enum-classes")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
|||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
declare namespace JS_TESTS {
|
||||||
|
type Nullable<T> = T | null | undefined
|
||||||
|
namespace foo {
|
||||||
|
/** @deprecated message 2 */
|
||||||
|
const bar: string;
|
||||||
|
/** @deprecated message 1 */
|
||||||
|
function foo(): void;
|
||||||
|
/** @deprecated message 3 */
|
||||||
|
class TestClass {
|
||||||
|
constructor();
|
||||||
|
}
|
||||||
|
class AnotherClass {
|
||||||
|
/** @deprecated message 4 */
|
||||||
|
constructor(value: string);
|
||||||
|
get value(): string;
|
||||||
|
/** @deprecated message 5 */
|
||||||
|
static fromNothing(): foo.AnotherClass;
|
||||||
|
static fromInt(value: number): foo.AnotherClass;
|
||||||
|
/** @deprecated message 6 */
|
||||||
|
foo(): void;
|
||||||
|
baz(): void;
|
||||||
|
/** @deprecated message 7 */
|
||||||
|
get bar(): string;
|
||||||
|
}
|
||||||
|
interface TestInterface {
|
||||||
|
/** @deprecated message 8 */
|
||||||
|
foo(): void;
|
||||||
|
bar(): void;
|
||||||
|
/** @deprecated message 9 */
|
||||||
|
readonly baz: string;
|
||||||
|
readonly __doNotUseOrImplementIt: {
|
||||||
|
readonly "foo.TestInterface": unique symbol;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const TestObject: {
|
||||||
|
/** @deprecated message 10 */
|
||||||
|
foo(): void;
|
||||||
|
bar(): void;
|
||||||
|
/** @deprecated message 11 */
|
||||||
|
get baz(): string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
+60
@@ -0,0 +1,60 @@
|
|||||||
|
/** This file is generated by {@link :js:js.test:generateJsExportOnFileTestFilesForTS} task. DO NOT MODIFY MANUALLY */
|
||||||
|
|
||||||
|
// CHECK_TYPESCRIPT_DECLARATIONS
|
||||||
|
// RUN_PLAIN_BOX_FUNCTION
|
||||||
|
// SKIP_MINIFICATION
|
||||||
|
// SKIP_NODE_JS
|
||||||
|
// INFER_MAIN_MODULE
|
||||||
|
// MODULE: JS_TESTS
|
||||||
|
// FILE: deprecated.kt
|
||||||
|
|
||||||
|
@file:JsExport
|
||||||
|
|
||||||
|
package foo
|
||||||
|
|
||||||
|
|
||||||
|
@Deprecated("message 1")
|
||||||
|
fun foo() {}
|
||||||
|
|
||||||
|
|
||||||
|
@Deprecated("message 2")
|
||||||
|
val bar: String = "Test"
|
||||||
|
|
||||||
|
|
||||||
|
@Deprecated("message 3")
|
||||||
|
class TestClass
|
||||||
|
|
||||||
|
|
||||||
|
class AnotherClass @Deprecated("message 4") constructor(val value: String) {
|
||||||
|
@JsName("fromNothing")
|
||||||
|
@Deprecated("message 5") constructor(): this("Test")
|
||||||
|
|
||||||
|
@JsName("fromInt")
|
||||||
|
constructor(value: Int): this(value.toString())
|
||||||
|
|
||||||
|
@Deprecated("message 6")
|
||||||
|
fun foo() {}
|
||||||
|
|
||||||
|
fun baz() {}
|
||||||
|
|
||||||
|
@Deprecated("message 7")
|
||||||
|
val bar: String = "Test"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
interface TestInterface {
|
||||||
|
@Deprecated("message 8")
|
||||||
|
fun foo()
|
||||||
|
fun bar()
|
||||||
|
@Deprecated("message 9")
|
||||||
|
val baz: String
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
object TestObject {
|
||||||
|
@Deprecated("message 10")
|
||||||
|
fun foo() {}
|
||||||
|
fun bar() {}
|
||||||
|
@Deprecated("message 11")
|
||||||
|
val baz: String = "Test"
|
||||||
|
}
|
||||||
Vendored
+13
@@ -0,0 +1,13 @@
|
|||||||
|
"use strict";
|
||||||
|
var bar = JS_TESTS.foo.bar;
|
||||||
|
var foo = JS_TESTS.foo.foo;
|
||||||
|
function assert(condition) {
|
||||||
|
if (!condition) {
|
||||||
|
throw "Assertion failed";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function box() {
|
||||||
|
assert(bar == "Test");
|
||||||
|
assert(foo() == undefined);
|
||||||
|
return "OK";
|
||||||
|
}
|
||||||
Vendored
+15
@@ -0,0 +1,15 @@
|
|||||||
|
import bar = JS_TESTS.foo.bar
|
||||||
|
import foo = JS_TESTS.foo.foo
|
||||||
|
|
||||||
|
function assert(condition: boolean) {
|
||||||
|
if (!condition) {
|
||||||
|
throw "Assertion failed";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function box(): string {
|
||||||
|
assert(bar == "Test");
|
||||||
|
assert(foo() == undefined);
|
||||||
|
|
||||||
|
return "OK";
|
||||||
|
}
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"include": [ "./*" ],
|
||||||
|
"extends": "../common.tsconfig.json"
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
declare namespace JS_TESTS {
|
||||||
|
type Nullable<T> = T | null | undefined
|
||||||
|
namespace foo {
|
||||||
|
/** @deprecated message 2 */
|
||||||
|
const bar: string;
|
||||||
|
/** @deprecated message 1 */
|
||||||
|
function foo(): void;
|
||||||
|
/** @deprecated message 3 */
|
||||||
|
class TestClass {
|
||||||
|
constructor();
|
||||||
|
}
|
||||||
|
class AnotherClass {
|
||||||
|
/** @deprecated message 4 */
|
||||||
|
constructor(value: string);
|
||||||
|
get value(): string;
|
||||||
|
/** @deprecated message 5 */
|
||||||
|
static fromNothing(): foo.AnotherClass;
|
||||||
|
static fromInt(value: number): foo.AnotherClass;
|
||||||
|
/** @deprecated message 6 */
|
||||||
|
foo(): void;
|
||||||
|
baz(): void;
|
||||||
|
/** @deprecated message 7 */
|
||||||
|
get bar(): string;
|
||||||
|
}
|
||||||
|
interface TestInterface {
|
||||||
|
/** @deprecated message 8 */
|
||||||
|
foo(): void;
|
||||||
|
bar(): void;
|
||||||
|
/** @deprecated message 9 */
|
||||||
|
readonly baz: string;
|
||||||
|
readonly __doNotUseOrImplementIt: {
|
||||||
|
readonly "foo.TestInterface": unique symbol;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const TestObject: {
|
||||||
|
/** @deprecated message 10 */
|
||||||
|
foo(): void;
|
||||||
|
bar(): void;
|
||||||
|
/** @deprecated message 11 */
|
||||||
|
get baz(): string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
// CHECK_TYPESCRIPT_DECLARATIONS
|
||||||
|
// RUN_PLAIN_BOX_FUNCTION
|
||||||
|
// SKIP_MINIFICATION
|
||||||
|
// SKIP_NODE_JS
|
||||||
|
// INFER_MAIN_MODULE
|
||||||
|
// MODULE: JS_TESTS
|
||||||
|
// FILE: deprecated.kt
|
||||||
|
|
||||||
|
package foo
|
||||||
|
|
||||||
|
@JsExport
|
||||||
|
@Deprecated("message 1")
|
||||||
|
fun foo() {}
|
||||||
|
|
||||||
|
@JsExport
|
||||||
|
@Deprecated("message 2")
|
||||||
|
val bar: String = "Test"
|
||||||
|
|
||||||
|
@JsExport
|
||||||
|
@Deprecated("message 3")
|
||||||
|
class TestClass
|
||||||
|
|
||||||
|
@JsExport
|
||||||
|
class AnotherClass @Deprecated("message 4") constructor(val value: String) {
|
||||||
|
@JsName("fromNothing")
|
||||||
|
@Deprecated("message 5") constructor(): this("Test")
|
||||||
|
|
||||||
|
@JsName("fromInt")
|
||||||
|
constructor(value: Int): this(value.toString())
|
||||||
|
|
||||||
|
@Deprecated("message 6")
|
||||||
|
fun foo() {}
|
||||||
|
|
||||||
|
fun baz() {}
|
||||||
|
|
||||||
|
@Deprecated("message 7")
|
||||||
|
val bar: String = "Test"
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsExport
|
||||||
|
interface TestInterface {
|
||||||
|
@Deprecated("message 8")
|
||||||
|
fun foo()
|
||||||
|
fun bar()
|
||||||
|
@Deprecated("message 9")
|
||||||
|
val baz: String
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsExport
|
||||||
|
object TestObject {
|
||||||
|
@Deprecated("message 10")
|
||||||
|
fun foo() {}
|
||||||
|
fun bar() {}
|
||||||
|
@Deprecated("message 11")
|
||||||
|
val baz: String = "Test"
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
"use strict";
|
||||||
|
var bar = JS_TESTS.foo.bar;
|
||||||
|
var foo = JS_TESTS.foo.foo;
|
||||||
|
function assert(condition) {
|
||||||
|
if (!condition) {
|
||||||
|
throw "Assertion failed";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function box() {
|
||||||
|
assert(bar == "Test");
|
||||||
|
assert(foo() == undefined);
|
||||||
|
return "OK";
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import bar = JS_TESTS.foo.bar
|
||||||
|
import foo = JS_TESTS.foo.foo
|
||||||
|
|
||||||
|
function assert(condition: boolean) {
|
||||||
|
if (!condition) {
|
||||||
|
throw "Assertion failed";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function box(): string {
|
||||||
|
assert(bar == "Test");
|
||||||
|
assert(foo() == undefined);
|
||||||
|
|
||||||
|
return "OK";
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"include": [ "./*" ],
|
||||||
|
"extends": "../common.tsconfig.json"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user