Support properties in @PublishedApi bridge quickfix

This commit is contained in:
Mikhael Bogdanov
2016-12-19 16:13:04 +01:00
parent 6ca1d47207
commit c38b55612b
12 changed files with 182 additions and 40 deletions
@@ -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;
}
}
@@ -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
}()
}
}
@@ -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")