Quickfix for published api
This commit is contained in:
@@ -171,6 +171,7 @@ interface DescriptorRendererOptions {
|
|||||||
var withDefinedIn: Boolean
|
var withDefinedIn: Boolean
|
||||||
var modifiers: Set<DescriptorRendererModifier>
|
var modifiers: Set<DescriptorRendererModifier>
|
||||||
var startFromName: Boolean
|
var startFromName: Boolean
|
||||||
|
var startFromDeclarationKeyword: Boolean
|
||||||
var debugMode: Boolean
|
var debugMode: Boolean
|
||||||
var classWithPrimaryConstructor: Boolean
|
var classWithPrimaryConstructor: Boolean
|
||||||
var verbose: Boolean
|
var verbose: Boolean
|
||||||
|
|||||||
@@ -643,24 +643,26 @@ internal class DescriptorRendererImpl(
|
|||||||
/* FUNCTIONS */
|
/* FUNCTIONS */
|
||||||
private fun renderFunction(function: FunctionDescriptor, builder: StringBuilder) {
|
private fun renderFunction(function: FunctionDescriptor, builder: StringBuilder) {
|
||||||
if (!startFromName) {
|
if (!startFromName) {
|
||||||
builder.renderAnnotations(function)
|
if (!startFromDeclarationKeyword) {
|
||||||
renderVisibility(function.visibility, builder)
|
builder.renderAnnotations(function)
|
||||||
renderModalityForCallable(function, builder)
|
renderVisibility(function.visibility, builder)
|
||||||
|
renderModalityForCallable(function, builder)
|
||||||
|
|
||||||
if (includeAdditionalModifiers) {
|
if (includeAdditionalModifiers) {
|
||||||
renderAdditionalModifiers(function, builder)
|
renderAdditionalModifiers(function, builder)
|
||||||
}
|
|
||||||
|
|
||||||
renderOverride(function, builder)
|
|
||||||
renderMemberKind(function, builder)
|
|
||||||
|
|
||||||
if (verbose) {
|
|
||||||
if (function.isHiddenToOvercomeSignatureClash) {
|
|
||||||
builder.append("/*isHiddenToOvercomeSignatureClash*/ ")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (function.isHiddenForResolutionEverywhereBesideSupercalls) {
|
renderOverride(function, builder)
|
||||||
builder.append("/*isHiddenForResolutionEverywhereBesideSupercalls*/ ")
|
renderMemberKind(function, builder)
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
if (function.isHiddenToOvercomeSignatureClash) {
|
||||||
|
builder.append("/*isHiddenToOvercomeSignatureClash*/ ")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (function.isHiddenForResolutionEverywhereBesideSupercalls) {
|
||||||
|
builder.append("/*isHiddenForResolutionEverywhereBesideSupercalls*/ ")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ internal class DescriptorRendererOptionsImpl : DescriptorRendererOptions {
|
|||||||
override var withDefinedIn by property(true)
|
override var withDefinedIn by property(true)
|
||||||
override var modifiers: Set<DescriptorRendererModifier> by property(DescriptorRendererModifier.DEFAULTS)
|
override var modifiers: Set<DescriptorRendererModifier> by property(DescriptorRendererModifier.DEFAULTS)
|
||||||
override var startFromName by property(false)
|
override var startFromName by property(false)
|
||||||
|
override var startFromDeclarationKeyword by property(false)
|
||||||
override var debugMode by property(false)
|
override var debugMode by property(false)
|
||||||
override var classWithPrimaryConstructor by property(false)
|
override var classWithPrimaryConstructor by property(false)
|
||||||
override var verbose by property(false)
|
override var verbose by property(false)
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createVariable.CreateP
|
|||||||
import org.jetbrains.kotlin.idea.quickfix.migration.MigrateTypeParameterListFix
|
import org.jetbrains.kotlin.idea.quickfix.migration.MigrateTypeParameterListFix
|
||||||
import org.jetbrains.kotlin.idea.quickfix.replaceWith.DeprecatedSymbolUsageFix
|
import org.jetbrains.kotlin.idea.quickfix.replaceWith.DeprecatedSymbolUsageFix
|
||||||
import org.jetbrains.kotlin.idea.quickfix.replaceWith.DeprecatedSymbolUsageInWholeProjectFix
|
import org.jetbrains.kotlin.idea.quickfix.replaceWith.DeprecatedSymbolUsageInWholeProjectFix
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.replaceWith.ReplaceProtectedToPublishedApiCallFix
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens.*
|
import org.jetbrains.kotlin.lexer.KtTokens.*
|
||||||
import org.jetbrains.kotlin.psi.KtClass
|
import org.jetbrains.kotlin.psi.KtClass
|
||||||
@@ -379,6 +380,7 @@ class QuickFixRegistrar : QuickFixContributor {
|
|||||||
|
|
||||||
DEPRECATION.registerFactory(DeprecatedSymbolUsageFix, DeprecatedSymbolUsageInWholeProjectFix)
|
DEPRECATION.registerFactory(DeprecatedSymbolUsageFix, DeprecatedSymbolUsageInWholeProjectFix)
|
||||||
DEPRECATION_ERROR.registerFactory(DeprecatedSymbolUsageFix, DeprecatedSymbolUsageInWholeProjectFix)
|
DEPRECATION_ERROR.registerFactory(DeprecatedSymbolUsageFix, DeprecatedSymbolUsageInWholeProjectFix)
|
||||||
|
PROTECTED_CALL_FROM_PUBLIC_INLINE.registerFactory(ReplaceProtectedToPublishedApiCallFix)
|
||||||
|
|
||||||
POSITIONED_VALUE_ARGUMENT_FOR_JAVA_ANNOTATION.registerFactory(ReplaceJavaAnnotationPositionedArgumentsFix)
|
POSITIONED_VALUE_ARGUMENT_FOR_JAVA_ANNOTATION.registerFactory(ReplaceJavaAnnotationPositionedArgumentsFix)
|
||||||
|
|
||||||
|
|||||||
+97
@@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.quickfix.replaceWith
|
||||||
|
|
||||||
|
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.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
|
||||||
|
import org.jetbrains.kotlin.diagnostics.Errors
|
||||||
|
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
||||||
|
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.psiUtil.createSmartPointer
|
||||||
|
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
|
||||||
|
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||||
|
|
||||||
|
class ReplaceProtectedToPublishedApiCallFix(
|
||||||
|
element: KtExpression,
|
||||||
|
val classOwnerPointer: SmartPsiElementPointer<KtClass>,
|
||||||
|
val originalName: String,
|
||||||
|
val paramNames: Map<String, String>,
|
||||||
|
val signature: String
|
||||||
|
) : KotlinQuickFixAction<KtExpression>(element) {
|
||||||
|
|
||||||
|
override fun getFamilyName() = "Replace with @PublishedApi bridge call"
|
||||||
|
|
||||||
|
val newFunctionName = "`${originalName.newName}`"
|
||||||
|
|
||||||
|
override fun getText() = "Replace with generated @PublishedApi bridge call '$newFunctionName(...)'"
|
||||||
|
|
||||||
|
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
||||||
|
val element = element ?: return
|
||||||
|
val isPublishedFunctionAlreadyExists = false/*TODO*/
|
||||||
|
if (!isPublishedFunctionAlreadyExists) {
|
||||||
|
val classOwner = classOwnerPointer.element ?: return
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
element.replace(KtPsiFactory(element).createExpression(newFunctionName))
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object : KotlinSingleIntentionActionFactory() {
|
||||||
|
val signatureRenderer = IdeDescriptorRenderers.SOURCE_CODE.withOptions {
|
||||||
|
renderDefaultValues = false
|
||||||
|
startFromDeclarationKeyword = true
|
||||||
|
withoutReturnType = true
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
val String.newName: String
|
||||||
|
get() = "access\$$this"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// "Replace with generated @PublishedApi bridge call '`access$test`(...)'" "true"
|
||||||
|
annotation class Z
|
||||||
|
|
||||||
|
open class ABase {
|
||||||
|
@Z
|
||||||
|
protected fun String.test(p: Int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline fun test() {
|
||||||
|
{
|
||||||
|
"123".<caret>test(1)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
// "Replace with generated @PublishedApi bridge call '`access$test`(...)'" "true"
|
||||||
|
annotation class Z
|
||||||
|
|
||||||
|
open class ABase {
|
||||||
|
@Z
|
||||||
|
protected fun String.test(p: Int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline fun test() {
|
||||||
|
{
|
||||||
|
"123".`access$test`(1)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
|
internal fun String.`access$test`(p: Int) = test(p)
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
// "Replace with generated @PublishedApi bridge call '`access$test`(...)'" "true"
|
||||||
|
annotation class Z
|
||||||
|
|
||||||
|
open class ABase<T> {
|
||||||
|
@Z
|
||||||
|
protected fun test(p: T) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fun param(): T {
|
||||||
|
return null!!
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun test() {
|
||||||
|
{
|
||||||
|
<caret>test(param())
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// "Replace with generated @PublishedApi bridge call '`access$test`(...)'" "true"
|
||||||
|
annotation class Z
|
||||||
|
|
||||||
|
open class ABase<T> {
|
||||||
|
@Z
|
||||||
|
protected fun test(p: T) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fun param(): T {
|
||||||
|
return null!!
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun test() {
|
||||||
|
{
|
||||||
|
`access$test`(param())
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
|
internal fun `access$test`(p: T) = test(p)
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
// "Replace with generated @PublishedApi bridge call '`access$test`(...)'" "true"
|
||||||
|
open class ABase<T> {
|
||||||
|
protected fun test(p: T) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fun param(): T {
|
||||||
|
return null!!
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
open class A : ABase<String>() {
|
||||||
|
|
||||||
|
inline fun test() {
|
||||||
|
{
|
||||||
|
<caret>test(param())
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
// "Replace with generated @PublishedApi bridge call '`access$test`(...)'" "true"
|
||||||
|
open class ABase<T> {
|
||||||
|
protected fun test(p: T) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fun param(): T {
|
||||||
|
return null!!
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
open class A : ABase<String>() {
|
||||||
|
|
||||||
|
inline fun test() {
|
||||||
|
{
|
||||||
|
`access$test`(param())
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
|
internal fun `access$test`(p: String) = test(p)
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// "Replace with generated @PublishedApi bridge call '`access$test`(...)'" "true"
|
||||||
|
annotation class Z
|
||||||
|
|
||||||
|
open class ABase {
|
||||||
|
@Z
|
||||||
|
protected fun <T> test(p: T): T {
|
||||||
|
null!!
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline fun test() {
|
||||||
|
{
|
||||||
|
//TODO remove generic
|
||||||
|
<caret>test<String>("123")
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
// "Replace with generated @PublishedApi bridge call '`access$test`(...)'" "true"
|
||||||
|
annotation class Z
|
||||||
|
|
||||||
|
open class ABase {
|
||||||
|
@Z
|
||||||
|
protected fun <T> test(p: T): T {
|
||||||
|
null!!
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline fun test() {
|
||||||
|
{
|
||||||
|
//TODO remove generic
|
||||||
|
`access$test`<String>("123")
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
|
internal fun <T> `access$test`(p: String) = test(p)
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// "Replace with generated @PublishedApi bridge call '`access$test`(...)'" "true"
|
||||||
|
annotation class Z
|
||||||
|
|
||||||
|
open class ABase {
|
||||||
|
@Z
|
||||||
|
protected fun test(p: Int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline fun test() {
|
||||||
|
{
|
||||||
|
<caret>test(1)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
// "Replace with generated @PublishedApi bridge call '`access$test`(...)'" "true"
|
||||||
|
annotation class Z
|
||||||
|
|
||||||
|
open class ABase {
|
||||||
|
@Z
|
||||||
|
protected fun test(p: Int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline fun test() {
|
||||||
|
{
|
||||||
|
`access$test`(1)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
|
internal fun `access$test`(p: Int) = test(p)
|
||||||
|
}
|
||||||
@@ -4855,6 +4855,45 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/publishedApi")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class PublishedApi extends AbstractQuickFixTest {
|
||||||
|
public void testAllFilesPresentInPublishedApi() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/deprecatedSymbolUsage/publishedApi"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("extension.kt")
|
||||||
|
public void testExtension() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/publishedApi/extension.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("generic.kt")
|
||||||
|
public void testGeneric() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/publishedApi/generic.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("genericDerived.kt")
|
||||||
|
public void testGenericDerived() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/publishedApi/genericDerived.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("genericFunction.kt")
|
||||||
|
public void testGenericFunction() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/publishedApi/genericFunction.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("simple.kt")
|
||||||
|
public void testSimple() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/publishedApi/simple.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/safeCall")
|
@TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/safeCall")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user