Quickfix for published api
This commit is contained in:
@@ -171,6 +171,7 @@ interface DescriptorRendererOptions {
|
||||
var withDefinedIn: Boolean
|
||||
var modifiers: Set<DescriptorRendererModifier>
|
||||
var startFromName: Boolean
|
||||
var startFromDeclarationKeyword: Boolean
|
||||
var debugMode: Boolean
|
||||
var classWithPrimaryConstructor: Boolean
|
||||
var verbose: Boolean
|
||||
|
||||
@@ -643,24 +643,26 @@ internal class DescriptorRendererImpl(
|
||||
/* FUNCTIONS */
|
||||
private fun renderFunction(function: FunctionDescriptor, builder: StringBuilder) {
|
||||
if (!startFromName) {
|
||||
builder.renderAnnotations(function)
|
||||
renderVisibility(function.visibility, builder)
|
||||
renderModalityForCallable(function, builder)
|
||||
if (!startFromDeclarationKeyword) {
|
||||
builder.renderAnnotations(function)
|
||||
renderVisibility(function.visibility, builder)
|
||||
renderModalityForCallable(function, builder)
|
||||
|
||||
if (includeAdditionalModifiers) {
|
||||
renderAdditionalModifiers(function, builder)
|
||||
}
|
||||
|
||||
renderOverride(function, builder)
|
||||
renderMemberKind(function, builder)
|
||||
|
||||
if (verbose) {
|
||||
if (function.isHiddenToOvercomeSignatureClash) {
|
||||
builder.append("/*isHiddenToOvercomeSignatureClash*/ ")
|
||||
if (includeAdditionalModifiers) {
|
||||
renderAdditionalModifiers(function, builder)
|
||||
}
|
||||
|
||||
if (function.isHiddenForResolutionEverywhereBesideSupercalls) {
|
||||
builder.append("/*isHiddenForResolutionEverywhereBesideSupercalls*/ ")
|
||||
renderOverride(function, builder)
|
||||
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 modifiers: Set<DescriptorRendererModifier> by property(DescriptorRendererModifier.DEFAULTS)
|
||||
override var startFromName by property(false)
|
||||
override var startFromDeclarationKeyword by property(false)
|
||||
override var debugMode by property(false)
|
||||
override var classWithPrimaryConstructor 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.replaceWith.DeprecatedSymbolUsageFix
|
||||
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.psi.KtClass
|
||||
@@ -379,6 +380,7 @@ class QuickFixRegistrar : QuickFixContributor {
|
||||
|
||||
DEPRECATION.registerFactory(DeprecatedSymbolUsageFix, DeprecatedSymbolUsageInWholeProjectFix)
|
||||
DEPRECATION_ERROR.registerFactory(DeprecatedSymbolUsageFix, DeprecatedSymbolUsageInWholeProjectFix)
|
||||
PROTECTED_CALL_FROM_PUBLIC_INLINE.registerFactory(ReplaceProtectedToPublishedApiCallFix)
|
||||
|
||||
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")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user