Push Down: Conflict analysis

This commit is contained in:
Alexey Sedunov
2015-08-11 14:05:27 +03:00
parent a66ef47887
commit 96f255225b
22 changed files with 502 additions and 34 deletions
@@ -29,6 +29,7 @@ import com.intellij.openapi.editor.markup.HighlighterTargetArea
import com.intellij.openapi.editor.markup.RangeHighlighter
import com.intellij.openapi.editor.markup.TextAttributes
import com.intellij.openapi.options.ConfigurationException
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.JavaProjectRootsUtil
import com.intellij.openapi.ui.popup.JBPopup
@@ -638,4 +639,10 @@ public fun JetElement.validateElement(errorMessage: String) {
catch(e: Exception) {
throw ConfigurationException(errorMessage)
}
}
public fun <T : Any> Project.runSynchronouslyWithProgress(progressTitle: String, canBeCanceled: Boolean, action: () -> T): T? {
var result: T? = null
ProgressManager.getInstance().runProcessWithProgressSynchronously( { result = action() }, progressTitle, canBeCanceled, this)
return result
}
@@ -74,7 +74,7 @@ private val CALLABLE_RENDERER = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_I
startFromName = false
}
private fun DeclarationDescriptor.renderForConflicts(): String {
fun DeclarationDescriptor.renderForConflicts(): String {
return when (this) {
is ClassDescriptor -> "${DescriptorRenderer.getClassKindPrefix(this)} ${IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(this)}"
is FunctionDescriptor -> "function '${CALLABLE_RENDERER.render(this)}'"
@@ -17,6 +17,8 @@
package org.jetbrains.kotlin.idea.refactoring.pushDown
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.util.Ref
import com.intellij.psi.PsiElement
import com.intellij.refactoring.BaseRefactoringProcessor
import com.intellij.refactoring.RefactoringBundle
@@ -31,10 +33,12 @@ import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
import org.jetbrains.kotlin.idea.core.refactoring.runSynchronouslyWithProgress
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo
import org.jetbrains.kotlin.idea.refactoring.pullUp.*
import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest
import org.jetbrains.kotlin.idea.search.declarationsSearch.searchInheritors
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
@@ -45,25 +49,32 @@ import org.jetbrains.kotlin.util.findCallableMemberBySignature
import org.jetbrains.kotlin.utils.keysToMap
import java.util.ArrayList
public class KotlinPushDownProcessor(
project: Project,
private val sourceClass: JetClass,
private val membersToMove: List<KotlinMemberInfo>
) : BaseRefactoringProcessor(project) {
private val resolutionFacade = sourceClass.getResolutionFacade()
public class KotlinPushDownContext(
val sourceClass: JetClass,
val membersToMove: List<KotlinMemberInfo>
) {
val resolutionFacade = sourceClass.getResolutionFacade()
private val sourceClassContext = resolutionFacade.analyzeFullyAndGetResult(listOf(sourceClass)).bindingContext
val sourceClassContext = resolutionFacade.analyzeFullyAndGetResult(listOf(sourceClass)).bindingContext
private val sourceClassDescriptor = sourceClassContext[BindingContext.DECLARATION_TO_DESCRIPTOR, sourceClass] as ClassDescriptor
val sourceClassDescriptor = sourceClassContext[BindingContext.DECLARATION_TO_DESCRIPTOR, sourceClass] as ClassDescriptor
private val memberDescriptors = membersToMove
val memberDescriptors = membersToMove
.map { it.member }
.keysToMap { sourceClassContext[BindingContext.DECLARATION_TO_DESCRIPTOR, it]!! }
}
public class KotlinPushDownProcessor(
project: Project,
sourceClass: JetClass,
membersToMove: List<KotlinMemberInfo>
) : BaseRefactoringProcessor(project) {
private val context = KotlinPushDownContext(sourceClass, membersToMove)
inner class UsageViewDescriptorImpl : UsageViewDescriptor {
override fun getProcessedElementsHeader() = RefactoringBundle.message("push.down.members.elements.header")
override fun getElements() = arrayOf(sourceClass)
override fun getElements() = arrayOf(context.sourceClass)
override fun getCodeReferencesText(usagesCount: Int, filesCount: Int) =
RefactoringBundle.message("classes.to.push.down.members.to", UsageViewBundle.getReferencesString(usagesCount, filesCount))
@@ -78,8 +89,8 @@ public class KotlinPushDownProcessor(
override fun createUsageViewDescriptor(usages: Array<out UsageInfo>) = UsageViewDescriptorImpl()
override fun getBeforeData() = RefactoringEventData().apply {
addElement(sourceClass)
addElements(membersToMove.map { it.member }.toTypedArray())
addElement(context.sourceClass)
addElements(context.membersToMove.map { it.member }.toTypedArray())
}
override fun getAfterData(usages: Array<out UsageInfo>) = RefactoringEventData().apply {
@@ -87,7 +98,7 @@ public class KotlinPushDownProcessor(
}
override fun findUsages(): Array<out UsageInfo> {
return HierarchySearchRequest(sourceClass, sourceClass.useScope, false)
return HierarchySearchRequest(context.sourceClass, context.sourceClass.useScope, false)
.searchInheritors()
.map { it.unwrapped }
.filterNotNull()
@@ -95,13 +106,29 @@ public class KotlinPushDownProcessor(
.toTypedArray()
}
override fun preprocessUsages(refUsages: Ref<Array<UsageInfo>>): Boolean {
val usages = refUsages.get() ?: UsageInfo.EMPTY_ARRAY
if (usages.isEmpty()) {
val message = "${context.sourceClassDescriptor.renderForConflicts()} doesn't have inheritors\n" +
"Pushing members down will result in them being deleted. Would you like to proceed?"
val answer = Messages.showYesNoDialog(message.capitalize(), PUSH_MEMBERS_DOWN, Messages.getWarningIcon())
if (answer == Messages.NO) return false
}
val conflicts = myProject.runSynchronouslyWithProgress(RefactoringBundle.message("detecting.possible.conflicts"), true) {
runReadAction { analyzePushDownConflicts(context, usages) }
} ?: return false
return showConflicts(conflicts, usages)
}
private fun pushDownToClass(targetClass: JetClassOrObject) {
val targetClassDescriptor = resolutionFacade.resolveToDescriptor(targetClass) as ClassDescriptor
val substitutor = getTypeSubstitutor(sourceClassDescriptor.defaultType, targetClassDescriptor.defaultType)
val targetClassDescriptor = context.resolutionFacade.resolveToDescriptor(targetClass) as ClassDescriptor
val substitutor = getTypeSubstitutor(context.sourceClassDescriptor.defaultType, targetClassDescriptor.defaultType)
?: TypeSubstitutor.EMPTY
members@ for (memberInfo in membersToMove) {
members@ for (memberInfo in context.membersToMove) {
val member = memberInfo.member
val memberDescriptor = memberDescriptors[member] ?: continue
val memberDescriptor = context.memberDescriptors[member] ?: continue
val movedMember = when (member) {
is JetProperty, is JetNamedFunction -> {
@@ -122,7 +149,7 @@ public class KotlinPushDownProcessor(
addModifierWithSpace(JetTokens.OVERRIDE_KEYWORD)
}
} ?: addMemberToTarget(member, targetClass).apply {
if (sourceClassDescriptor.kind == ClassKind.INTERFACE) {
if (context.sourceClassDescriptor.kind == ClassKind.INTERFACE) {
if (targetClassDescriptor.kind != ClassKind.INTERFACE && memberDescriptor.modality == Modality.ABSTRACT) {
addModifierWithSpace(JetTokens.ABSTRACT_KEYWORD)
}
@@ -138,8 +165,11 @@ public class KotlinPushDownProcessor(
is JetClassOrObject -> {
if (memberInfo.overrides != null) {
sourceClass.getDelegatorToSuperClassByDescriptor(memberDescriptor as ClassDescriptor, sourceClassContext)?.let {
addDelegatorToSuperClass(it, targetClass, targetClassDescriptor, sourceClassContext, substitutor)
context.sourceClass.getDelegatorToSuperClassByDescriptor(
memberDescriptor as ClassDescriptor,
context.sourceClassContext
)?.let {
addDelegatorToSuperClass(it, targetClass, targetClassDescriptor, context.sourceClassContext, substitutor)
}
continue@members
}
@@ -155,9 +185,9 @@ public class KotlinPushDownProcessor(
}
private fun removeOriginalMembers() {
for (memberInfo in membersToMove) {
for (memberInfo in context.membersToMove) {
val member = memberInfo.member
val memberDescriptor = memberDescriptors[member] ?: continue
val memberDescriptor = context.memberDescriptors[member] ?: continue
when (member) {
is JetProperty, is JetNamedFunction -> {
member as JetCallableDeclaration
@@ -167,7 +197,7 @@ public class KotlinPushDownProcessor(
if (member.hasModifier(JetTokens.PRIVATE_KEYWORD)) {
member.addModifierWithSpace(JetTokens.PROTECTED_KEYWORD)
}
makeAbstract(member, memberDescriptor, TypeSubstitutor.EMPTY, sourceClass)
makeAbstract(member, memberDescriptor, TypeSubstitutor.EMPTY, context.sourceClass)
member.typeReference?.addToShorteningWaitSet()
}
else {
@@ -176,8 +206,11 @@ public class KotlinPushDownProcessor(
}
is JetClassOrObject -> {
if (memberInfo.overrides != null) {
sourceClass.getDelegatorToSuperClassByDescriptor(memberDescriptor as ClassDescriptor, sourceClassContext)?.let {
sourceClass.removeDelegationSpecifier(it)
context.sourceClass.getDelegatorToSuperClassByDescriptor(
memberDescriptor as ClassDescriptor,
context.sourceClassContext
)?.let {
context.sourceClass.removeDelegationSpecifier(it)
}
}
else {
@@ -191,7 +224,9 @@ public class KotlinPushDownProcessor(
override fun performRefactoring(usages: Array<out UsageInfo>) {
val markedElements = ArrayList<JetElement>()
try {
membersToMove.forEach { markedElements += markElements(it.member, sourceClassContext, sourceClassDescriptor, null) }
context.membersToMove.forEach {
markedElements += markElements(it.member, context.sourceClassContext, context.sourceClassDescriptor, null)
}
usages.forEach { (it.element as? JetClassOrObject)?.let { pushDownToClass(it) } }
removeOriginalMembers()
}
@@ -0,0 +1,220 @@
/*
* Copyright 2010-2015 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.refactoring.pushDown
import com.intellij.psi.PsiElement
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.refactoring.util.CommonRefactoringUtil
import com.intellij.refactoring.util.RefactoringUIUtil
import com.intellij.usageView.UsageInfo
import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.refactoring.pullUp.renderForConflicts
import org.jetbrains.kotlin.idea.references.JetReference
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForReceiver
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.calls.callUtil.getCalleeExpressionIfAny
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.scopes.receivers.QualifierReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.substitutions.getTypeSubstitutor
import org.jetbrains.kotlin.util.findCallableMemberBySignature
import java.util.ArrayList
fun analyzePushDownConflicts(context: KotlinPushDownContext,
usages: Array<out UsageInfo>): MultiMap<PsiElement, String> {
val targetClasses = usages.map { it.element?.unwrapped }.filterNotNull()
val conflicts = MultiMap<PsiElement, String>()
val membersToPush = ArrayList<JetNamedDeclaration>()
val membersToKeepAbstract = ArrayList<JetNamedDeclaration>()
for (info in context.membersToMove) {
val member = info.member
if (!info.isChecked || (member is JetClassOrObject && info.overrides != null)) continue
membersToPush += member
if ((member is JetNamedFunction || member is JetProperty)
&& info.isToAbstract
&& (context.memberDescriptors[member] as CallableMemberDescriptor).modality != Modality.ABSTRACT) {
membersToKeepAbstract += member
}
}
for (targetClass in targetClasses) {
checkConflicts(conflicts, context, targetClass, membersToKeepAbstract, membersToPush)
}
return conflicts
}
private fun checkConflicts(
conflicts: MultiMap<PsiElement, String>,
context: KotlinPushDownContext,
targetClass: PsiElement,
membersToKeepAbstract: List<JetNamedDeclaration>,
membersToPush: ArrayList<JetNamedDeclaration>
) {
if (targetClass !is JetClassOrObject) {
conflicts.putValue(
targetClass,
"Non-Kotlin ${RefactoringUIUtil.getDescription(targetClass, false)} won't be affected by the refactoring"
)
return
}
val targetClassDescriptor = context.resolutionFacade.resolveToDescriptor(targetClass) as ClassDescriptor
val substitutor = getTypeSubstitutor(context.sourceClassDescriptor.defaultType, targetClassDescriptor.defaultType)
?: TypeSubstitutor.EMPTY
if (!context.sourceClass.isInterface() && targetClass is JetClass && targetClass.isInterface()) {
val message = "${targetClassDescriptor.renderForConflicts()} " +
"inherits from ${context.sourceClassDescriptor.renderForConflicts()}.\n" +
"It won't be affected by the refactoring"
conflicts.putValue(targetClass, message.capitalize())
}
for (member in membersToPush) {
checkMemberClashing(conflicts, context, member, membersToKeepAbstract, substitutor, targetClass, targetClassDescriptor)
checkSuperCalls(conflicts, context, member, membersToPush)
checkExternalUsages(conflicts, context, member, targetClassDescriptor)
checkVisibility(conflicts, context, member, targetClassDescriptor)
}
}
private fun checkMemberClashing(
conflicts: MultiMap<PsiElement, String>,
context: KotlinPushDownContext,
member: JetNamedDeclaration,
membersToKeepAbstract: List<JetNamedDeclaration>,
substitutor: TypeSubstitutor,
targetClass: JetClassOrObject,
targetClassDescriptor: ClassDescriptor) {
when (member) {
is JetNamedFunction, is JetProperty -> {
val memberDescriptor = context.memberDescriptors[member] as CallableMemberDescriptor
val clashingDescriptor = targetClassDescriptor.findCallableMemberBySignature(memberDescriptor.substitute(substitutor) as CallableMemberDescriptor)
val clashingDeclaration = clashingDescriptor?.source?.getPsi() as? JetNamedDeclaration
if (clashingDescriptor != null && clashingDeclaration != null) {
if (memberDescriptor.modality != Modality.ABSTRACT && member !in membersToKeepAbstract) {
val message = "${targetClassDescriptor.renderForConflicts()} already contains ${clashingDescriptor.renderForConflicts()}"
conflicts.putValue(clashingDeclaration, CommonRefactoringUtil.capitalize(message))
}
if (!clashingDeclaration.hasModifier(JetTokens.OVERRIDE_KEYWORD)) {
val message = "${clashingDescriptor.renderForConflicts()} in ${targetClassDescriptor.renderForConflicts()} " +
"will override corresponding member of ${context.sourceClassDescriptor.renderForConflicts()} " +
"after refactoring"
conflicts.putValue(clashingDeclaration, CommonRefactoringUtil.capitalize(message))
}
}
}
is JetClassOrObject -> {
targetClass.declarations
.filterIsInstance<JetClassOrObject>()
.firstOrNull() { it.name == member.name }
?.let {
val message = "${targetClassDescriptor.renderForConflicts()} " +
"already contains nested class named ${CommonRefactoringUtil.htmlEmphasize(member.name)}"
conflicts.putValue(it, message.capitalize())
}
}
}
}
private fun checkSuperCalls(
conflicts: MultiMap<PsiElement, String>,
context: KotlinPushDownContext,
member: JetNamedDeclaration,
membersToPush: ArrayList<JetNamedDeclaration>
) {
member.accept(
object : JetTreeVisitorVoid() {
override fun visitSuperExpression(expression: JetSuperExpression) {
val qualifiedExpression = expression.getQualifiedExpressionForReceiver() ?: return
val refExpr = qualifiedExpression.selectorExpression.getCalleeExpressionIfAny() as? JetSimpleNameExpression ?: return
for (descriptor in refExpr.mainReference.resolveToDescriptors(context.sourceClassContext)) {
val memberDescriptor = descriptor as? CallableMemberDescriptor ?: continue
val containingClass = memberDescriptor.containingDeclaration as? ClassDescriptor ?: continue
if (!DescriptorUtils.isSubclass(context.sourceClassDescriptor, containingClass)) continue
val memberInSource = context.sourceClassDescriptor.findCallableMemberBySignature(memberDescriptor)?.source?.getPsi()
?: continue
if (memberInSource !in membersToPush) {
conflicts.putValue(qualifiedExpression,
"Pushed member won't be available in '${qualifiedExpression.text}'")
}
}
}
}
)
}
private fun checkExternalUsages(
conflicts: MultiMap<PsiElement, String>,
context: KotlinPushDownContext,
member: JetNamedDeclaration,
targetClassDescriptor: ClassDescriptor
) {
for (ref in ReferencesSearch.search(member, member.resolveScope, false)) {
val calleeExpr = ref.element as? JetSimpleNameExpression ?: continue
val resolvedCall = calleeExpr.getResolvedCall(context.resolutionFacade.analyze(calleeExpr)) ?: continue
val callElement = resolvedCall.call.callElement
val dispatchReceiver = resolvedCall.dispatchReceiver
if (!dispatchReceiver.exists() || dispatchReceiver is QualifierReceiver) continue
val receiverClassDescriptor = dispatchReceiver.type.constructor.declarationDescriptor as? ClassDescriptor ?: continue
if (!DescriptorUtils.isSubclass(receiverClassDescriptor, targetClassDescriptor)) {
conflicts.putValue(callElement, "Pushed member won't be available in '${callElement.text}'")
}
}
}
private fun checkVisibility(
conflicts: MultiMap<PsiElement, String>,
context: KotlinPushDownContext,
member: JetNamedDeclaration,
targetClassDescriptor: ClassDescriptor
) {
fun reportConflictIfAny(targetDescriptor: DeclarationDescriptor) {
val target = (targetDescriptor as? DeclarationDescriptorWithSource)?.source?.getPsi() ?: return
if (targetDescriptor is DeclarationDescriptorWithVisibility
&& !Visibilities.isVisible(ReceiverValue.IRRELEVANT_RECEIVER, targetDescriptor, targetClassDescriptor)) {
val message = "${context.memberDescriptors[member]!!.renderForConflicts()} " +
"uses ${targetDescriptor.renderForConflicts()}, " +
"which is not accessible from the ${targetClassDescriptor.renderForConflicts()}"
conflicts.putValue(target, message.capitalize())
}
}
member.accept(
object : JetTreeVisitorVoid() {
override fun visitReferenceExpression(expression: JetReferenceExpression) {
super.visitReferenceExpression(expression)
expression.references
.flatMap { (it as? JetReference)?.resolveToDescriptors(context.sourceClassContext) ?: emptyList() }
.forEach(::reportConflictIfAny)
}
}
)
}
@@ -0,0 +1,24 @@
abstract class <caret>A {
// INFO: {"checked": "true"}
abstract val y: Boolean
// INFO: {"checked": "true", "toAbstract": "true"}
val z: Int = 1
// INFO: {"checked": "true"}
abstract fun foo(n: Int, m: Int)
// INFO: {"checked": "true", "toAbstract": "true"}
fun foo(b: Boolean) = !b
}
class B : A {
val x: Int = 2
val y: Boolean get() = x > 0
val z: Int = 3
fun foo(n: Int) = n + 2
fun foo(n: Int, m: Int) = n + m
fun foo(b: Boolean) = true
}
@@ -0,0 +1,4 @@
Function 'fun foo(Boolean): Boolean' in class B will override corresponding member of class A after refactoring
Function 'fun foo(Int, Int): Int' in class B will override corresponding member of class A after refactoring
Property 'val y: Boolean' in class B will override corresponding member of class A after refactoring
Property 'val z: Int' in class B will override corresponding member of class A after refactoring
+32
View File
@@ -0,0 +1,32 @@
abstract class <caret>A {
// INFO: {"checked": "true"}
open val x: Int = 1
// INFO: {"checked": "true"}
abstract val y: Boolean
// INFO: {"checked": "true", "toAbstract": "true"}
open val z: Int = 1
// INFO: {"checked": "true"}
open fun foo(n: Int) = n + 1
// INFO: {"checked": "true"}
abstract fun foo(n: Int, m: Int)
// INFO: {"checked": "true", "toAbstract": "true"}
open fun foo(b: Boolean) = !b
// INFO: {"checked": "true"}
class X
}
class B : A {
override val x: Int = 2
override val y: Boolean get() = x > 0
override val z: Int = 3
override fun foo(n: Int) = n + 2
override fun foo(n: Int, m: Int) = n + m
override fun foo(b: Boolean) = true
class X
}
@@ -0,0 +1,3 @@
Class B already contains function 'fun foo(Int): Int'
Class B already contains nested class named X
Class B already contains property 'val x: Int'
@@ -0,0 +1,6 @@
open class <caret>A {
// INFO: {"checked": "true"}
val x: Int
}
interface I : A
@@ -0,0 +1,2 @@
Interface I inherits from class A.
It won't be affected by the refactoring
@@ -0,0 +1,30 @@
open class A {
open fun foo(n: Int) {
}
open fun bar(n: Int) {
}
}
open class <caret>B : A() {
// INFO: {"checked": "true"}
fun foo() {
super.foo(1)
super.bar(1)
}
// INFO: {"checked": "true"}
override fun foo(n: Int) {
}
override fun bar(n: Int) {
}
}
class C : B() {
}
@@ -0,0 +1 @@
Pushed member won't be available in 'super.bar(1)'
+3
View File
@@ -0,0 +1,3 @@
class B extends A {
}
+3
View File
@@ -0,0 +1,3 @@
open class <caret>A {
val x: Int = 1
}
@@ -0,0 +1 @@
Non-Kotlin class B won't be affected by the refactoring
+4 -4
View File
@@ -1,12 +1,12 @@
open class <caret>A {
// INFO: {"checked": "true", "toAbstract": "true"}
private fun foo() {
private open fun foo() {
}
}
class B : A {
fun foo() {
inner class B : A() {
override fun foo() {
}
}
}
+3 -3
View File
@@ -1,10 +1,10 @@
open class A {
// INFO: {"checked": "true", "toAbstract": "true"}
protected abstract fun foo()
}
class B : A {
override fun foo() {
inner class B : A() {
override fun foo() {
}
}
}
@@ -0,0 +1,14 @@
open class <caret>A {
private fun foo() {
}
// INFO: {"checked": "true"}
fun bar() {
foo()
}
}
class B : A() {
}
@@ -0,0 +1 @@
Function 'fun bar()' uses function 'fun foo()', which is not accessible from the class B
@@ -0,0 +1,37 @@
open class <caret>A {
fun foo() {
}
// INFO: {"checked": "true"}
fun foo(n: Int) {
}
fun test() {
foo()
foo(1)
}
}
open class B : A() {
}
class C : B() {
}
fun A.test() {
foo()
foo(2)
}
fun test() {
A().foo()
A().foo(3)
B().foo()
B().foo(4)
C().foo()
C().foo(5)
}
@@ -0,0 +1,3 @@
Pushed member won't be available in 'foo(1)'
Pushed member won't be available in 'foo(2)'
Pushed member won't be available in 'foo(3)'
@@ -31,10 +31,34 @@ import java.util.regex.Pattern;
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class PushDownTestGenerated extends AbstractPushDownTest {
@TestMetadata("accidentalOverrides.kt")
public void testAccidentalOverrides() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/accidentalOverrides.kt");
doTest(fileName);
}
public void testAllFilesPresentInPushDown() throws Exception {
JetTestUtils.assertAllTestsPresentInSingleGeneratedClass(this.getClass(), new File("idea/testData/refactoring/pushDown"), Pattern.compile("^(.+)\\.kt$"));
}
@TestMetadata("clashingMembers.kt")
public void testClashingMembers() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/clashingMembers.kt");
doTest(fileName);
}
@TestMetadata("classToInterface.kt")
public void testClassToInterface() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/classToInterface.kt");
doTest(fileName);
}
@TestMetadata("conflictingSuperCall.kt")
public void testConflictingSuperCall() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/conflictingSuperCall.kt");
doTest(fileName);
}
@TestMetadata("finalClass.kt")
public void testFinalClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/finalClass.kt");
@@ -47,6 +71,12 @@ public class PushDownTestGenerated extends AbstractPushDownTest {
doTest(fileName);
}
@TestMetadata("kotlinToJava.kt")
public void testKotlinToJava() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/kotlinToJava.kt");
doTest(fileName);
}
@TestMetadata("liftPrivate.kt")
public void testLiftPrivate() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/liftPrivate.kt");
@@ -101,6 +131,18 @@ public class PushDownTestGenerated extends AbstractPushDownTest {
doTest(fileName);
}
@TestMetadata("pushMembersUsingPrivates.kt")
public void testPushMembersUsingPrivates() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/pushMembersUsingPrivates.kt");
doTest(fileName);
}
@TestMetadata("pushMembersWithExternalUsages.kt")
public void testPushMembersWithExternalUsages() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/pushMembersWithExternalUsages.kt");
doTest(fileName);
}
@TestMetadata("pushSuperInterfaces.kt")
public void testPushSuperInterfaces() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/pushSuperInterfaces.kt");