Support properties in @PublishedApi bridge quickfix
This commit is contained in:
@@ -268,9 +268,7 @@ public class JvmCodegenUtil {
|
||||
|
||||
@NotNull
|
||||
public static CallableMemberDescriptor getDirectMember(@NotNull CallableMemberDescriptor descriptor) {
|
||||
return descriptor instanceof PropertyAccessorDescriptor
|
||||
? ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty()
|
||||
: descriptor;
|
||||
return DescriptorUtils.getDirectMember(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) {
|
||||
String facadeFqName = getPackageMemberOwnerInternalName((DeserializedCallableMemberDescriptor) directMember, publicFacade);
|
||||
@@ -946,7 +946,7 @@ public class KotlinTypeMapper {
|
||||
return null;
|
||||
}
|
||||
|
||||
descriptor = getDirectMember(descriptor);
|
||||
descriptor = DescriptorUtils.getDirectMember(descriptor);
|
||||
assert descriptor instanceof DeserializedCallableMemberDescriptor :
|
||||
"Descriptor without sources should be instance of DeserializedCallableMemberDescriptor, but: " +
|
||||
descriptor;
|
||||
@@ -1039,7 +1039,7 @@ public class KotlinTypeMapper {
|
||||
writeVoidReturn(sw);
|
||||
}
|
||||
else {
|
||||
CallableMemberDescriptor directMember = getDirectMember(f);
|
||||
CallableMemberDescriptor directMember = DescriptorUtils.getDirectMember(f);
|
||||
KotlinType thisIfNeeded = null;
|
||||
if (OwnerKind.DEFAULT_IMPLS == kind) {
|
||||
ReceiverTypeAndTypeParameters receiverTypeAndTypeParameters = TypeMapperUtilsKt.patchTypeParametersForDefaultImplMethod(directMember);
|
||||
|
||||
@@ -841,18 +841,20 @@ internal class DescriptorRendererImpl(
|
||||
|
||||
private fun renderProperty(property: PropertyDescriptor, builder: StringBuilder) {
|
||||
if (!startFromName) {
|
||||
builder.renderAnnotations(property)
|
||||
renderVisibility(property.visibility, builder)
|
||||
if (!startFromDeclarationKeyword) {
|
||||
builder.renderAnnotations(property)
|
||||
renderVisibility(property.visibility, builder)
|
||||
|
||||
if (property.isConst) {
|
||||
builder.append("const ")
|
||||
if (property.isConst) {
|
||||
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)
|
||||
renderTypeParameters(property.typeParameters, builder, true)
|
||||
renderReceiver(property, builder)
|
||||
|
||||
@@ -600,4 +600,12 @@ public class DescriptorUtils {
|
||||
|
||||
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.project.Project
|
||||
import com.intellij.psi.SmartPsiElementPointer
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
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.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.js.descriptorUtils.getJetTypeFqName
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
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
|
||||
|
||||
class ReplaceProtectedToPublishedApiCallFix(
|
||||
@@ -44,31 +41,44 @@ class ReplaceProtectedToPublishedApiCallFix(
|
||||
val classOwnerPointer: SmartPsiElementPointer<KtClass>,
|
||||
val originalName: String,
|
||||
val paramNames: Map<String, String>,
|
||||
val signature: String
|
||||
val signature: String,
|
||||
val isProperty: Boolean,
|
||||
val isVar: Boolean
|
||||
) : KotlinQuickFixAction<KtExpression>(element) {
|
||||
|
||||
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) {
|
||||
val element = element ?: return
|
||||
val isPublishedFunctionAlreadyExists = false/*TODO*/
|
||||
if (!isPublishedFunctionAlreadyExists) {
|
||||
val isPublishedMemberAlreadyExists = false/*TODO*/
|
||||
if (!isPublishedMemberAlreadyExists) {
|
||||
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" +
|
||||
"internal "+//${extensionType?.let { it + "." } ?: ""}$newFunctionName(${paramNames.entries.map { it.key + " :" + it.value }.joinToString(", ")}) = " +
|
||||
signature.replaceFirst("$originalName(", "$newFunctionName(") +
|
||||
" = $originalName(${paramNames.keys.map { it }.joinToString(", ")})"
|
||||
)
|
||||
val newFunction = classOwner.addDeclaration(function)
|
||||
ShortenReferences.DEFAULT.process(newFunction)
|
||||
}
|
||||
else {
|
||||
KtPsiFactory(classOwner).createFunction(
|
||||
"@kotlin.PublishedApi\n" +
|
||||
"internal " + signature.replaceFirst("$originalName(", "$newName(") +
|
||||
" = $originalName(${paramNames.keys.map { it }.joinToString(", ")})"
|
||||
)
|
||||
}
|
||||
|
||||
ShortenReferences.DEFAULT.process(classOwner.addDeclaration(newMember))
|
||||
}
|
||||
element.replace(KtPsiFactory(element).createExpression(newFunctionName))
|
||||
element.replace(KtPsiFactory(element).createExpression(newName))
|
||||
}
|
||||
|
||||
companion object : KotlinSingleIntentionActionFactory() {
|
||||
@@ -80,15 +90,19 @@ class ReplaceProtectedToPublishedApiCallFix(
|
||||
|
||||
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
||||
val psiElement = diagnostic.psiElement as? KtExpression ?: return null
|
||||
val descriptor = DiagnosticFactory.cast(diagnostic, Errors.PROTECTED_CALL_FROM_PUBLIC_INLINE).a
|
||||
val isProperty = descriptor is PropertyAccessorDescriptor || descriptor is PropertyDescriptor
|
||||
if (isProperty) return null/*TODO support properties*/
|
||||
val descriptor = DiagnosticFactory.cast(diagnostic, Errors.PROTECTED_CALL_FROM_PUBLIC_INLINE).a.let {
|
||||
if (it is CallableMemberDescriptor) DescriptorUtils.getDirectMember(it) else it
|
||||
}
|
||||
val isProperty = descriptor is PropertyDescriptor
|
||||
val isVar = descriptor is PropertyDescriptor && descriptor.isVar
|
||||
|
||||
val signature = signatureRenderer.render(descriptor)
|
||||
val paramNameAndType = descriptor.valueParameters.associate { it.name.asString() to it.type.getJetTypeFqName(false)}
|
||||
val classDescriptor = descriptor.containingDeclaration as? ClassDescriptor ?: 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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@TestMetadata("extensionVar.kt")
|
||||
public void testExtensionVar() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/publishedApi/extensionVar.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("generic.kt")
|
||||
public void testGeneric() throws Exception {
|
||||
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");
|
||||
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")
|
||||
|
||||
Reference in New Issue
Block a user