Retain javascript name from @native annotation
As the first part of KT-15257 JS: quickfix to migrate from @native to external
This commit is contained in:
+55
-17
@@ -34,6 +34,7 @@ import org.jetbrains.kotlin.idea.util.addAnnotation
|
|||||||
import org.jetbrains.kotlin.js.PredefinedAnnotation
|
import org.jetbrains.kotlin.js.PredefinedAnnotation
|
||||||
import org.jetbrains.kotlin.js.resolve.diagnostics.ErrorsJs
|
import org.jetbrains.kotlin.js.resolve.diagnostics.ErrorsJs
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
@@ -53,31 +54,48 @@ class MigrateExternalExtensionFix(declaration: KtNamedDeclaration)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fixExtensionMemberDeclaration(declaration: KtNamedDeclaration, project: Project, editor: Editor?, file: KtFile) {
|
private data class JsNativeAnnotations(val annotations: List<KtAnnotationEntry>, val nativeAnnotation: KtAnnotationEntry?, val isGetter: Boolean, val isSetter: Boolean, val isInvoke: Boolean)
|
||||||
val name = declaration.nameAsSafeName
|
|
||||||
val annotationEntries = declaration.modifierList?.annotationEntries
|
private fun fetchJsNativeAnnotations(declaration: KtNamedDeclaration) : JsNativeAnnotations {
|
||||||
val isGetter = annotationEntries?.any { it.isJsAnnotation(PredefinedAnnotation.NATIVE_GETTER) } ?: false
|
var isGetter: Boolean = false
|
||||||
val isSetter = annotationEntries?.any { it.isJsAnnotation(PredefinedAnnotation.NATIVE_SETTER) } ?: false
|
var isSetter: Boolean = false
|
||||||
val isInvoke = annotationEntries?.any { it.isJsAnnotation(PredefinedAnnotation.NATIVE_INVOKE) } ?: false
|
var isInvoke: Boolean = false
|
||||||
annotationEntries?.filter { it.isJsNativeAnnotation() }?.forEach { it.delete() }
|
var nativeAnnotation: KtAnnotationEntry? = null
|
||||||
declaration.addModifier(KtTokens.INLINE_KEYWORD)
|
val nativeAnnotations = ArrayList<KtAnnotationEntry>()
|
||||||
declaration.removeModifier(KtTokens.EXTERNAL_KEYWORD)
|
|
||||||
if (declaration is KtFunction) {
|
declaration.modifierList?.annotationEntries?.forEach {
|
||||||
declaration.addAnnotation(KotlinBuiltIns.FQ_NAMES.suppress.toSafe(), "\"NOTHING_TO_INLINE\"")
|
if (it.isJsAnnotation(PredefinedAnnotation.NATIVE_GETTER)) {
|
||||||
if (!declaration.hasDeclaredReturnType() && !isSetter && !isInvoke) {
|
isGetter = true
|
||||||
SpecifyTypeExplicitlyIntention.addTypeAnnotation(editor, declaration, declaration.builtIns.unitType)
|
nativeAnnotations.add(it)
|
||||||
|
} else if (it.isJsAnnotation(PredefinedAnnotation.NATIVE_SETTER)) {
|
||||||
|
isSetter = true
|
||||||
|
nativeAnnotations.add(it)
|
||||||
|
} else if (it.isJsAnnotation(PredefinedAnnotation.NATIVE_INVOKE)) {
|
||||||
|
isInvoke = true
|
||||||
|
nativeAnnotations.add(it)
|
||||||
|
} else if (it.isJsAnnotation(PredefinedAnnotation.NATIVE)) {
|
||||||
|
nativeAnnotations.add(it)
|
||||||
|
nativeAnnotation = it
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return JsNativeAnnotations(nativeAnnotations, nativeAnnotation, isGetter, isSetter, isInvoke)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fixExtensionMemberDeclaration(declaration: KtNamedDeclaration, project: Project, editor: Editor?, file: KtFile) {
|
||||||
|
val name = declaration.nameAsSafeName
|
||||||
|
val annotations = fetchJsNativeAnnotations(declaration)
|
||||||
|
fixAnnotations(declaration, annotations, editor)
|
||||||
|
|
||||||
val ktPsiFactory = KtPsiFactory(project)
|
val ktPsiFactory = KtPsiFactory(project)
|
||||||
val body = ktPsiFactory.buildExpression {
|
val body = ktPsiFactory.buildExpression {
|
||||||
appendName(Name.identifier("asDynamic"))
|
appendName(Name.identifier("asDynamic"))
|
||||||
if (isGetter) {
|
if (annotations.isGetter) {
|
||||||
appendFixedText("()")
|
appendFixedText("()")
|
||||||
if (declaration is KtNamedFunction) {
|
if (declaration is KtNamedFunction) {
|
||||||
appendParameters(declaration, "[", "]")
|
appendParameters(declaration, "[", "]")
|
||||||
}
|
}
|
||||||
} else if (isSetter) {
|
} else if (annotations.isSetter) {
|
||||||
appendFixedText("()")
|
appendFixedText("()")
|
||||||
if (declaration is KtNamedFunction) {
|
if (declaration is KtNamedFunction) {
|
||||||
appendParameters(declaration, "[", "]", skipLast = true)
|
appendParameters(declaration, "[", "]", skipLast = true)
|
||||||
@@ -86,7 +104,7 @@ class MigrateExternalExtensionFix(declaration: KtNamedDeclaration)
|
|||||||
appendName(it)
|
appendName(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (isInvoke) {
|
} else if (annotations.isInvoke) {
|
||||||
appendFixedText("()")
|
appendFixedText("()")
|
||||||
if (declaration is KtNamedFunction) {
|
if (declaration is KtNamedFunction) {
|
||||||
appendParameters(declaration, "(", ")")
|
appendParameters(declaration, "(", ")")
|
||||||
@@ -104,7 +122,7 @@ class MigrateExternalExtensionFix(declaration: KtNamedDeclaration)
|
|||||||
declaration.bodyExpression?.delete()
|
declaration.bodyExpression?.delete()
|
||||||
declaration.equalsToken?.delete()
|
declaration.equalsToken?.delete()
|
||||||
|
|
||||||
if (isSetter || isInvoke) {
|
if (annotations.isSetter || annotations.isInvoke) {
|
||||||
val blockBody = ktPsiFactory.createSingleStatementBlock(body)
|
val blockBody = ktPsiFactory.createSingleStatementBlock(body)
|
||||||
declaration.add(blockBody)
|
declaration.add(blockBody)
|
||||||
} else {
|
} else {
|
||||||
@@ -135,6 +153,26 @@ class MigrateExternalExtensionFix(declaration: KtNamedDeclaration)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun fixAnnotations(declaration: KtNamedDeclaration, annotations: JsNativeAnnotations, editor: Editor?) {
|
||||||
|
annotations.annotations.forEach { it.delete() }
|
||||||
|
|
||||||
|
declaration.addModifier(KtTokens.INLINE_KEYWORD)
|
||||||
|
declaration.removeModifier(KtTokens.EXTERNAL_KEYWORD)
|
||||||
|
|
||||||
|
|
||||||
|
if (declaration is KtFunction) {
|
||||||
|
declaration.addAnnotation(KotlinBuiltIns.FQ_NAMES.suppress.toSafe(), "\"NOTHING_TO_INLINE\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (annotations.nativeAnnotation != null && annotations.nativeAnnotation.valueArguments.isNotEmpty()) {
|
||||||
|
declaration.addAnnotation(FqName("JsName"), annotations.nativeAnnotation.valueArguments.joinToString { it.asElement().text})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (declaration is KtFunction && !declaration.hasDeclaredReturnType() && !annotations.isSetter && !annotations.isInvoke) {
|
||||||
|
SpecifyTypeExplicitlyIntention.addTypeAnnotation(editor, declaration, declaration.builtIns.unitType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun BuilderByPattern<KtExpression>.appendParameters(declaration: KtNamedFunction, lParenth: String, rParenth: String, skipLast: Boolean = false) {
|
private fun BuilderByPattern<KtExpression>.appendParameters(declaration: KtNamedFunction, lParenth: String, rParenth: String, skipLast: Boolean = false) {
|
||||||
appendFixedText(lParenth)
|
appendFixedText(lParenth)
|
||||||
for ((index, param) in declaration.valueParameters.let { if (skipLast) it.take(it.size-1) else it }.withIndex()) {
|
for ((index, param) in declaration.valueParameters.let { if (skipLast) it.take(it.size-1) else it }.withIndex()) {
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// "Fix with 'asDynamic'" "true"
|
||||||
|
// JS
|
||||||
|
|
||||||
|
class A
|
||||||
|
|
||||||
|
<caret>@native("aaaa") fun A.foo(): Int
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// "Fix with 'asDynamic'" "true"
|
||||||
|
// JS
|
||||||
|
|
||||||
|
class A
|
||||||
|
|
||||||
|
@JsName("aaaa")
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
inline fun A.foo(): Int = asDynamic().foo()
|
||||||
@@ -6427,6 +6427,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nativeExtensionFunJsName.kt")
|
||||||
|
public void testNativeExtensionFunJsName() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/migration/jsExternal/nativeExtensionFunJsName.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("nativeExtensionFunJsRuntime.kt")
|
@TestMetadata("nativeExtensionFunJsRuntime.kt")
|
||||||
public void testNativeExtensionFunJsRuntime() throws Exception {
|
public void testNativeExtensionFunJsRuntime() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/migration/jsExternal/nativeExtensionFunJsRuntime.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/migration/jsExternal/nativeExtensionFunJsRuntime.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user