Support properties in @PublishedApi bridge quickfix
This commit is contained in:
@@ -268,9 +268,7 @@ public class JvmCodegenUtil {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static CallableMemberDescriptor getDirectMember(@NotNull CallableMemberDescriptor descriptor) {
|
public static CallableMemberDescriptor getDirectMember(@NotNull CallableMemberDescriptor descriptor) {
|
||||||
return descriptor instanceof PropertyAccessorDescriptor
|
return DescriptorUtils.getDirectMember(descriptor);
|
||||||
? ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty()
|
|
||||||
: descriptor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isArgumentWhichWillBeInlined(@NotNull BindingContext bindingContext, @NotNull DeclarationDescriptor descriptor) {
|
public static boolean isArgumentWhichWillBeInlined(@NotNull BindingContext bindingContext, @NotNull DeclarationDescriptor descriptor) {
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ public class KotlinTypeMapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CallableMemberDescriptor directMember = getDirectMember(descriptor);
|
CallableMemberDescriptor directMember = DescriptorUtils.getDirectMember(descriptor);
|
||||||
|
|
||||||
if (directMember instanceof DeserializedCallableMemberDescriptor) {
|
if (directMember instanceof DeserializedCallableMemberDescriptor) {
|
||||||
String facadeFqName = getPackageMemberOwnerInternalName((DeserializedCallableMemberDescriptor) directMember, publicFacade);
|
String facadeFqName = getPackageMemberOwnerInternalName((DeserializedCallableMemberDescriptor) directMember, publicFacade);
|
||||||
@@ -946,7 +946,7 @@ public class KotlinTypeMapper {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
descriptor = getDirectMember(descriptor);
|
descriptor = DescriptorUtils.getDirectMember(descriptor);
|
||||||
assert descriptor instanceof DeserializedCallableMemberDescriptor :
|
assert descriptor instanceof DeserializedCallableMemberDescriptor :
|
||||||
"Descriptor without sources should be instance of DeserializedCallableMemberDescriptor, but: " +
|
"Descriptor without sources should be instance of DeserializedCallableMemberDescriptor, but: " +
|
||||||
descriptor;
|
descriptor;
|
||||||
@@ -1039,7 +1039,7 @@ public class KotlinTypeMapper {
|
|||||||
writeVoidReturn(sw);
|
writeVoidReturn(sw);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
CallableMemberDescriptor directMember = getDirectMember(f);
|
CallableMemberDescriptor directMember = DescriptorUtils.getDirectMember(f);
|
||||||
KotlinType thisIfNeeded = null;
|
KotlinType thisIfNeeded = null;
|
||||||
if (OwnerKind.DEFAULT_IMPLS == kind) {
|
if (OwnerKind.DEFAULT_IMPLS == kind) {
|
||||||
ReceiverTypeAndTypeParameters receiverTypeAndTypeParameters = TypeMapperUtilsKt.patchTypeParametersForDefaultImplMethod(directMember);
|
ReceiverTypeAndTypeParameters receiverTypeAndTypeParameters = TypeMapperUtilsKt.patchTypeParametersForDefaultImplMethod(directMember);
|
||||||
|
|||||||
@@ -841,18 +841,20 @@ internal class DescriptorRendererImpl(
|
|||||||
|
|
||||||
private fun renderProperty(property: PropertyDescriptor, builder: StringBuilder) {
|
private fun renderProperty(property: PropertyDescriptor, builder: StringBuilder) {
|
||||||
if (!startFromName) {
|
if (!startFromName) {
|
||||||
builder.renderAnnotations(property)
|
if (!startFromDeclarationKeyword) {
|
||||||
renderVisibility(property.visibility, builder)
|
builder.renderAnnotations(property)
|
||||||
|
renderVisibility(property.visibility, builder)
|
||||||
|
|
||||||
if (property.isConst) {
|
if (property.isConst) {
|
||||||
builder.append("const ")
|
builder.append("const ")
|
||||||
|
}
|
||||||
|
|
||||||
|
renderExternal(property, builder)
|
||||||
|
renderModalityForCallable(property, builder)
|
||||||
|
renderOverride(property, builder)
|
||||||
|
renderLateInit(property, builder)
|
||||||
|
renderMemberKind(property, builder)
|
||||||
}
|
}
|
||||||
|
|
||||||
renderExternal(property, builder)
|
|
||||||
renderModalityForCallable(property, builder)
|
|
||||||
renderOverride(property, builder)
|
|
||||||
renderLateInit(property, builder)
|
|
||||||
renderMemberKind(property, builder)
|
|
||||||
renderValVarPrefix(property, builder)
|
renderValVarPrefix(property, builder)
|
||||||
renderTypeParameters(property.typeParameters, builder, true)
|
renderTypeParameters(property.typeParameters, builder, true)
|
||||||
renderReceiver(property, builder)
|
renderReceiver(property, builder)
|
||||||
|
|||||||
@@ -600,4 +600,12 @@ public class DescriptorUtils {
|
|||||||
|
|
||||||
throw new IllegalStateException("Function not found");
|
throw new IllegalStateException("Function not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static CallableMemberDescriptor getDirectMember(@NotNull CallableMemberDescriptor descriptor) {
|
||||||
|
return descriptor instanceof PropertyAccessorDescriptor
|
||||||
|
? ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty()
|
||||||
|
: descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-24
@@ -20,8 +20,8 @@ import com.intellij.codeInsight.intention.IntentionAction
|
|||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.psi.SmartPsiElementPointer
|
import com.intellij.psi.SmartPsiElementPointer
|
||||||
|
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor
|
|
||||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
|
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
|
||||||
@@ -31,12 +31,9 @@ import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction
|
|||||||
import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory
|
import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory
|
||||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||||
import org.jetbrains.kotlin.js.descriptorUtils.getJetTypeFqName
|
import org.jetbrains.kotlin.js.descriptorUtils.getJetTypeFqName
|
||||||
import org.jetbrains.kotlin.psi.KtClass
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.KtExpression
|
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
|
||||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer
|
import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer
|
||||||
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||||
|
|
||||||
class ReplaceProtectedToPublishedApiCallFix(
|
class ReplaceProtectedToPublishedApiCallFix(
|
||||||
@@ -44,31 +41,44 @@ class ReplaceProtectedToPublishedApiCallFix(
|
|||||||
val classOwnerPointer: SmartPsiElementPointer<KtClass>,
|
val classOwnerPointer: SmartPsiElementPointer<KtClass>,
|
||||||
val originalName: String,
|
val originalName: String,
|
||||||
val paramNames: Map<String, String>,
|
val paramNames: Map<String, String>,
|
||||||
val signature: String
|
val signature: String,
|
||||||
|
val isProperty: Boolean,
|
||||||
|
val isVar: Boolean
|
||||||
) : KotlinQuickFixAction<KtExpression>(element) {
|
) : KotlinQuickFixAction<KtExpression>(element) {
|
||||||
|
|
||||||
override fun getFamilyName() = "Replace with @PublishedApi bridge call"
|
override fun getFamilyName() = "Replace with @PublishedApi bridge call"
|
||||||
|
|
||||||
val newFunctionName = "`${originalName.newName}`"
|
val newName = "`${originalName.newName}`"
|
||||||
|
|
||||||
override fun getText() = "Replace with generated @PublishedApi bridge call '$newFunctionName(...)'"
|
override fun getText() = "Replace with generated @PublishedApi bridge call '$newName${if (!isProperty) "(...)" else ""}'"
|
||||||
|
|
||||||
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
||||||
val element = element ?: return
|
val element = element ?: return
|
||||||
val isPublishedFunctionAlreadyExists = false/*TODO*/
|
val isPublishedMemberAlreadyExists = false/*TODO*/
|
||||||
if (!isPublishedFunctionAlreadyExists) {
|
if (!isPublishedMemberAlreadyExists) {
|
||||||
val classOwner = classOwnerPointer.element ?: return
|
val classOwner = classOwnerPointer.element ?: return
|
||||||
|
val newMember: KtDeclaration =
|
||||||
|
if (isProperty) {
|
||||||
|
KtPsiFactory(classOwner).createProperty(
|
||||||
|
"@kotlin.PublishedApi\n" +
|
||||||
|
"internal " + signature.replaceFirst("$originalName:", "$newName:") +
|
||||||
|
"\n" +
|
||||||
|
"get() = $originalName\n" +
|
||||||
|
if (isVar) "set(value) { $originalName = value }" else ""
|
||||||
|
)
|
||||||
|
|
||||||
val function = KtPsiFactory(classOwner).createFunction(
|
}
|
||||||
"@kotlin.PublishedApi\n" +
|
else {
|
||||||
"internal "+//${extensionType?.let { it + "." } ?: ""}$newFunctionName(${paramNames.entries.map { it.key + " :" + it.value }.joinToString(", ")}) = " +
|
KtPsiFactory(classOwner).createFunction(
|
||||||
signature.replaceFirst("$originalName(", "$newFunctionName(") +
|
"@kotlin.PublishedApi\n" +
|
||||||
" = $originalName(${paramNames.keys.map { it }.joinToString(", ")})"
|
"internal " + signature.replaceFirst("$originalName(", "$newName(") +
|
||||||
)
|
" = $originalName(${paramNames.keys.map { it }.joinToString(", ")})"
|
||||||
val newFunction = classOwner.addDeclaration(function)
|
)
|
||||||
ShortenReferences.DEFAULT.process(newFunction)
|
}
|
||||||
|
|
||||||
|
ShortenReferences.DEFAULT.process(classOwner.addDeclaration(newMember))
|
||||||
}
|
}
|
||||||
element.replace(KtPsiFactory(element).createExpression(newFunctionName))
|
element.replace(KtPsiFactory(element).createExpression(newName))
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object : KotlinSingleIntentionActionFactory() {
|
companion object : KotlinSingleIntentionActionFactory() {
|
||||||
@@ -80,15 +90,19 @@ class ReplaceProtectedToPublishedApiCallFix(
|
|||||||
|
|
||||||
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
||||||
val psiElement = diagnostic.psiElement as? KtExpression ?: return null
|
val psiElement = diagnostic.psiElement as? KtExpression ?: return null
|
||||||
val descriptor = DiagnosticFactory.cast(diagnostic, Errors.PROTECTED_CALL_FROM_PUBLIC_INLINE).a
|
val descriptor = DiagnosticFactory.cast(diagnostic, Errors.PROTECTED_CALL_FROM_PUBLIC_INLINE).a.let {
|
||||||
val isProperty = descriptor is PropertyAccessorDescriptor || descriptor is PropertyDescriptor
|
if (it is CallableMemberDescriptor) DescriptorUtils.getDirectMember(it) else it
|
||||||
if (isProperty) return null/*TODO support properties*/
|
}
|
||||||
|
val isProperty = descriptor is PropertyDescriptor
|
||||||
|
val isVar = descriptor is PropertyDescriptor && descriptor.isVar
|
||||||
|
|
||||||
val signature = signatureRenderer.render(descriptor)
|
val signature = signatureRenderer.render(descriptor)
|
||||||
val paramNameAndType = descriptor.valueParameters.associate { it.name.asString() to it.type.getJetTypeFqName(false)}
|
val paramNameAndType = descriptor.valueParameters.associate { it.name.asString() to it.type.getJetTypeFqName(false)}
|
||||||
val classDescriptor = descriptor.containingDeclaration as? ClassDescriptor ?: return null
|
val classDescriptor = descriptor.containingDeclaration as? ClassDescriptor ?: return null
|
||||||
val source = classDescriptor.source.getPsi() as? KtClass ?: return null
|
val source = classDescriptor.source.getPsi() as? KtClass ?: return null
|
||||||
return ReplaceProtectedToPublishedApiCallFix(psiElement, source.createSmartPointer(), descriptor.name.asString(), paramNameAndType, signature)
|
return ReplaceProtectedToPublishedApiCallFix(
|
||||||
|
psiElement, source.createSmartPointer(), descriptor.name.asString(), paramNameAndType, signature, isProperty, isVar
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val String.newName: String
|
val String.newName: String
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
// "Replace with generated @PublishedApi bridge call '`access$prop`'" "true"
|
||||||
|
annotation class Z
|
||||||
|
|
||||||
|
open class ABase {
|
||||||
|
@Z
|
||||||
|
protected var String.prop: Int
|
||||||
|
get() = 1
|
||||||
|
set(field) = {}
|
||||||
|
|
||||||
|
|
||||||
|
inline fun test() {
|
||||||
|
{
|
||||||
|
"123".<caret>prop
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
// "Replace with generated @PublishedApi bridge call '`access$test`(...)'" "true"
|
||||||
|
annotation class Z
|
||||||
|
|
||||||
|
open class ABase {
|
||||||
|
@Z
|
||||||
|
protected var String.prop: Int
|
||||||
|
get() = 1
|
||||||
|
set(field) = {}
|
||||||
|
|
||||||
|
|
||||||
|
inline fun test() {
|
||||||
|
{
|
||||||
|
"123".`access$prop`
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
|
internal var String.`access$prop`: Int
|
||||||
|
get() = prop
|
||||||
|
set(value) {
|
||||||
|
prop = value
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// "Replace with generated @PublishedApi bridge call '`access$prop`'" "true"
|
||||||
|
annotation class Z
|
||||||
|
|
||||||
|
open class ABase {
|
||||||
|
@Z
|
||||||
|
protected val prop = 1
|
||||||
|
|
||||||
|
inline fun test() {
|
||||||
|
{
|
||||||
|
<caret>prop
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// "Replace with generated @PublishedApi bridge call '`access$prop`'" "true"
|
||||||
|
annotation class Z
|
||||||
|
|
||||||
|
open class ABase {
|
||||||
|
@Z
|
||||||
|
protected val prop = 1
|
||||||
|
|
||||||
|
inline fun test() {
|
||||||
|
{
|
||||||
|
`access$prop`
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
|
internal val `access$prop`: Int
|
||||||
|
get() = prop
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// "Replace with generated @PublishedApi bridge call '`access$prop`'" "true"
|
||||||
|
annotation class Z
|
||||||
|
|
||||||
|
open class ABase {
|
||||||
|
@Z
|
||||||
|
protected var prop = 1
|
||||||
|
|
||||||
|
inline fun test() {
|
||||||
|
{
|
||||||
|
<caret>prop
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
// "Replace with generated @PublishedApi bridge call '`access$prop`'" "true"
|
||||||
|
annotation class Z
|
||||||
|
|
||||||
|
open class ABase {
|
||||||
|
@Z
|
||||||
|
protected var prop = 1
|
||||||
|
|
||||||
|
inline fun test() {
|
||||||
|
{
|
||||||
|
`access$prop`
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
|
internal var `access$prop`: Int
|
||||||
|
get() = prop
|
||||||
|
set(value) {
|
||||||
|
prop = value
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4869,6 +4869,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("extensionVar.kt")
|
||||||
|
public void testExtensionVar() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/publishedApi/extensionVar.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("generic.kt")
|
@TestMetadata("generic.kt")
|
||||||
public void testGeneric() throws Exception {
|
public void testGeneric() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/publishedApi/generic.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/publishedApi/generic.kt");
|
||||||
@@ -4892,6 +4898,18 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
|||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/publishedApi/simple.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/publishedApi/simple.kt");
|
||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("simpleVal.kt")
|
||||||
|
public void testSimpleVal() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/publishedApi/simpleVal.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("simpleVar.kt")
|
||||||
|
public void testSimpleVar() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/publishedApi/simpleVar.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/safeCall")
|
@TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/safeCall")
|
||||||
|
|||||||
Reference in New Issue
Block a user