Add a set of new tests for KT-27075 (create expected class) + some fixes

This commit is contained in:
Mikhail Glukhikh
2018-11-20 13:48:48 +03:00
parent 4ab4358d22
commit f31428257d
37 changed files with 400 additions and 34 deletions
@@ -9,7 +9,6 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.descriptors.annotations.AnnotationWithTarget
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.name.Name
@@ -219,6 +218,7 @@ interface DescriptorRendererOptions {
var alwaysRenderModifiers: Boolean
var renderConstructorKeyword: Boolean
var renderUnabbreviatedType: Boolean
var renderTypeExpansions: Boolean
var includeAdditionalModifiers: Boolean
var parameterNamesInFunctionalTypes: Boolean
var renderFunctionContracts: Boolean
@@ -272,6 +272,8 @@ enum class DescriptorRendererModifier(val includeByDefault: Boolean) {
INLINE(true),
EXPECT(true),
ACTUAL(true),
CONST(true),
LATEINIT(true),
;
companion object {
@@ -129,10 +129,14 @@ internal class DescriptorRendererImpl(
private fun StringBuilder.renderNormalizedType(type: KotlinType) {
val abbreviated = type.unwrap() as? AbbreviatedType
if (abbreviated != null) {
// TODO nullability is lost for abbreviated type?
renderNormalizedTypeAsIs(abbreviated.abbreviation)
if (renderUnabbreviatedType) {
renderAbbreviatedTypeExpansion(abbreviated)
if (renderTypeExpansions) {
renderNormalizedTypeAsIs(abbreviated.expandedType)
} else {
// TODO nullability is lost for abbreviated type?
renderNormalizedTypeAsIs(abbreviated.abbreviation)
if (renderUnabbreviatedType) {
renderAbbreviatedTypeExpansion(abbreviated)
}
}
return
}
@@ -468,15 +472,16 @@ internal class DescriptorRendererImpl(
}
}
private fun renderVisibility(visibility: Visibility, builder: StringBuilder) {
private fun renderVisibility(visibility: Visibility, builder: StringBuilder): Boolean {
@Suppress("NAME_SHADOWING")
var visibility = visibility
if (DescriptorRendererModifier.VISIBILITY !in modifiers) return
if (DescriptorRendererModifier.VISIBILITY !in modifiers) return false
if (normalizedVisibilities) {
visibility = visibility.normalize()
}
if (!renderDefaultVisibility && visibility == Visibilities.DEFAULT_VISIBILITY) return
if (!renderDefaultVisibility && visibility == Visibilities.DEFAULT_VISIBILITY) return false
builder.append(renderKeyword(visibility.displayName)).append(" ")
return true
}
private fun renderModality(modality: Modality, builder: StringBuilder, defaultModality: Modality) {
@@ -719,15 +724,16 @@ internal class DescriptorRendererImpl(
private fun renderConstructor(constructor: ConstructorDescriptor, builder: StringBuilder) {
builder.renderAnnotations(constructor)
renderVisibility(constructor.visibility, builder)
val visibilityRendered = renderVisibility(constructor.visibility, builder)
renderMemberKind(constructor, builder)
if (renderConstructorKeyword) {
val constructorKeywordRendered = renderConstructorKeyword || !constructor.isPrimary || visibilityRendered
if (constructorKeywordRendered) {
builder.append(renderKeyword("constructor"))
}
val classDescriptor = constructor.containingDeclaration
if (secondaryConstructorsAsPrimary) {
if (renderConstructorKeyword) {
if (constructorKeywordRendered) {
builder.append(" ")
}
renderName(classDescriptor, builder, true)
@@ -861,11 +867,11 @@ internal class DescriptorRendererImpl(
if (!startFromDeclarationKeyword) {
renderPropertyAnnotations(property, builder)
renderVisibility(property.visibility, builder)
renderModifier(builder, property.isConst, "const")
renderModifier(builder, DescriptorRendererModifier.CONST in modifiers && property.isConst, "const")
renderMemberModifiers(property, builder)
renderModalityForCallable(property, builder)
renderOverride(property, builder)
renderModifier(builder, property.isLateInit, "lateinit")
renderModifier(builder, DescriptorRendererModifier.LATEINIT in modifiers && property.isLateInit, "lateinit")
renderMemberKind(property, builder)
}
renderValVarPrefix(property, builder)
@@ -116,6 +116,8 @@ internal class DescriptorRendererOptionsImpl : DescriptorRendererOptions {
override var renderUnabbreviatedType: Boolean by property(true)
override var renderTypeExpansions: Boolean by property(false)
override var includeAdditionalModifiers: Boolean by property(true)
override var parameterNamesInFunctionalTypes: Boolean by property(true)
@@ -41,6 +41,8 @@ import org.jetbrains.kotlin.psi.findDocComment.findDocComment
import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier
import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier
import org.jetbrains.kotlin.renderer.*
import org.jetbrains.kotlin.renderer.DescriptorRenderer.Companion.withOptions
import org.jetbrains.kotlin.renderer.DescriptorRendererModifier.*
import org.jetbrains.kotlin.resolve.checkers.ExperimentalUsageChecker
import org.jetbrains.kotlin.resolve.descriptorUtil.setSingleOverridden
import org.jetbrains.kotlin.util.findCallableMemberBySignature
@@ -118,8 +120,8 @@ interface OverrideMemberChooserObject : ClassMember {
}
fun OverrideMemberChooserObject.generateTopLevelActual(
copyDoc: Boolean,
project: Project
project: Project,
copyDoc: Boolean
) = generateMember(null, copyDoc, project, forceActual = true, forceExpect = false)
fun OverrideMemberChooserObject.generateActualMember(
@@ -128,8 +130,8 @@ fun OverrideMemberChooserObject.generateActualMember(
) = generateMember(targetClass, copyDoc, targetClass.project, forceActual = true, forceExpect = false)
fun OverrideMemberChooserObject.generateTopLevelExpect(
copyDoc: Boolean,
project: Project
project: Project,
copyDoc: Boolean
) = generateMember(null, copyDoc, project, forceActual = false, forceExpect = true)
fun OverrideMemberChooserObject.generateExpectMember(
@@ -177,8 +179,6 @@ private fun OverrideMemberChooserObject.generateMember(
forceActual -> newMember.addModifier(KtTokens.ACTUAL_KEYWORD)
forceExpect -> if (targetClass == null) {
newMember.addModifier(KtTokens.EXPECT_KEYWORD)
} else {
newMember.makeNotActual()
}
targetClass?.hasActualModifier() == true -> {
val expectClassDescriptors =
@@ -191,9 +191,6 @@ private fun OverrideMemberChooserObject.generateMember(
newMember.addModifier(KtTokens.ACTUAL_KEYWORD)
}
}
else -> {
newMember.makeNotActual()
}
}
if (copyDoc) {
@@ -215,9 +212,9 @@ private fun OverrideMemberChooserObject.generateMember(
return newMember
}
private val OVERRIDE_RENDERER = DescriptorRenderer.withOptions {
private val OVERRIDE_RENDERER = withOptions {
defaultParameterValueRenderer = null
modifiers = setOf(DescriptorRendererModifier.OVERRIDE, DescriptorRendererModifier.ANNOTATIONS)
modifiers = setOf(OVERRIDE, ANNOTATIONS)
withDefinedIn = false
classifierNamePolicy = ClassifierNamePolicy.SOURCE_CODE_QUALIFIED
overrideRenderingPolicy = OverrideRenderingPolicy.RENDER_OVERRIDE
@@ -232,17 +229,20 @@ private val OVERRIDE_RENDERER = DescriptorRenderer.withOptions {
}
private val EXPECT_RENDERER = OVERRIDE_RENDERER.withOptions {
modifiers = DescriptorRendererModifier.ALL
renderConstructorKeyword = true
modifiers = setOf(VISIBILITY, MODALITY, OVERRIDE, INNER, MEMBER_KIND)
renderConstructorKeyword = false
secondaryConstructorsAsPrimary = false
renderDefaultVisibility = false
renderDefaultModality = false
renderConstructorDelegation = true
renderAnnotationPropertiesInPrimaryConstructor = true
renderTypeExpansions = true
}
private val ACTUAL_RENDERER = EXPECT_RENDERER.withOptions {
modifiers += ACTUAL
actualPropertiesInPrimaryConstructor = true
renderTypeExpansions = false
renderConstructorDelegation = true
}
private fun PropertyDescriptor.wrap(forceOverride: Boolean): PropertyDescriptor {
@@ -282,7 +282,7 @@ private fun generateFunction(
return if (targetClass != null) {
memberChooserObject.generateActualMember(targetClass = targetClass, copyDoc = true)
} else {
memberChooserObject.generateTopLevelActual(copyDoc = true, project = project)
memberChooserObject.generateTopLevelActual(project = project, copyDoc = true)
} as KtFunction
}
@@ -299,7 +299,7 @@ private fun generateProperty(
return if (targetClass != null) {
memberChooserObject.generateActualMember(targetClass = targetClass, copyDoc = true)
} else {
memberChooserObject.generateTopLevelActual(copyDoc = true, project = project)
memberChooserObject.generateTopLevelActual(project = project, copyDoc = true)
} as KtProperty
}
@@ -188,6 +188,7 @@ private fun KtPsiFactory.generateClassOrObjectByActualClass(
} else {
expectedClass.makeNotActual()
}
expectedClass.removeModifier(KtTokens.DATA_KEYWORD)
declLoop@ for (actualDeclaration in actualClass.declarations) {
val descriptor = actualDeclaration.toDescriptor() ?: continue
@@ -199,6 +200,7 @@ private fun KtPsiFactory.generateClassOrObjectByActualClass(
continue@declLoop
}
is KtCallableDeclaration -> {
if (!actualDeclaration.hasActualModifier()) continue@declLoop
when (actualDeclaration) {
is KtFunction -> generateFunction(project, actualDeclaration, descriptor as FunctionDescriptor, expectedClass)
is KtProperty -> generateProperty(project, actualDeclaration, descriptor as PropertyDescriptor, expectedClass)
@@ -209,6 +211,14 @@ private fun KtPsiFactory.generateClassOrObjectByActualClass(
}
expectedClass.addDeclaration(expectedDeclaration)
}
if (!actualClass.isAnnotation()) {
for (actualProperty in actualClass.primaryConstructorParameters) {
if (!actualProperty.hasValOrVar() || !actualProperty.hasActualModifier()) continue
val descriptor = actualProperty.toDescriptor() as? PropertyDescriptor ?: continue
val expectedProperty = generateProperty(project, actualProperty, descriptor, expectedClass)
expectedClass.addDeclaration(expectedProperty)
}
}
val actualPrimaryConstructor = actualClass.primaryConstructor
if (expectedClass is KtClass && actualPrimaryConstructor != null) {
val descriptor = actualPrimaryConstructor.toDescriptor()
@@ -234,7 +244,7 @@ private fun generateFunction(
return if (targetClass != null) {
memberChooserObject.generateExpectMember(targetClass = targetClass, copyDoc = true)
} else {
memberChooserObject.generateTopLevelExpect(copyDoc = true, project = project)
memberChooserObject.generateTopLevelExpect(project = project, copyDoc = true)
} as KtFunction
}
@@ -251,7 +261,7 @@ private fun generateProperty(
return if (targetClass != null) {
memberChooserObject.generateExpectMember(targetClass = targetClass, copyDoc = true)
} else {
memberChooserObject.generateTopLevelExpect(copyDoc = true, project = project)
memberChooserObject.generateTopLevelExpect(project = project, copyDoc = true)
} as KtProperty
}
@@ -0,0 +1,2 @@
// My: to be implemented
// DISABLE-ERRORS
@@ -0,0 +1,24 @@
// My: to be implemented
// DISABLE-ERRORS
/**
* Dokka comment: class to be created as expect
*/
expect class My {
fun foo(param: String): Int
fun String.bar(y: Double): Boolean
/**
* Dokka comment: Just does nothing
*/
fun baz()
/**
* Dokka comment: Just does nothing
*
* @flag this parameter is just ignored
*/
constructor(flag: Boolean)
val isGood: Boolean
var status: Int
}
@@ -0,0 +1,35 @@
// "Create expected class in common module testModule_Common" "true"
// DISABLE-ERRORS
/**
* Dokka comment: class to be created as expect
*/
actual class <caret>My {
// Helpful function
actual fun foo(param: String): Int = 42
/* Very helpful extension */
actual fun String.bar(y: Double): Boolean = true
/**
* Dokka comment: Just does nothing
*/
actual fun baz() {}
/**
* Dokka comment: Just does nothing
*
* @flag this parameter is just ignored
*/
actual constructor(flag: Boolean) {}
// Some immutable property
actual val isGood: Boolean
get() = true
/* Interesting mutable property */
actual var status: Int
get() = 0
set(value) {}
}
@@ -0,0 +1,35 @@
// "Create expected class in common module testModule_Common" "true"
// DISABLE-ERRORS
/**
* Dokka comment: class to be created as expect
*/
actual class <caret>My {
// Helpful function
actual fun foo(param: String): Int = 42
/* Very helpful extension */
actual fun String.bar(y: Double): Boolean = true
/**
* Dokka comment: Just does nothing
*/
actual fun baz() {}
/**
* Dokka comment: Just does nothing
*
* @flag this parameter is just ignored
*/
actual constructor(flag: Boolean) {}
// Some immutable property
actual val isGood: Boolean
get() = true
/* Interesting mutable property */
actual var status: Int
get() = 0
set(value) {}
}
@@ -1,4 +1,4 @@
// "Create expected property in common module testModule_Common" "true"
// DISABLE-ERRORS
actual class My(actual val <caret>s: String)
actual class My(actual val s: String)
@@ -0,0 +1,5 @@
expect class My {
fun foo(): Int
val some: Double
}
@@ -0,0 +1,12 @@
// "Create expected class in common module testModule_Common" "true"
// DISABLE-ERRORS
import kotlin.Double as MyDouble
typealias MyInt = Int
actual class <caret>My {
actual fun foo(): MyInt = 42
actual val some: MyDouble = 4.0
}
@@ -0,0 +1,12 @@
// "Create expected class in common module testModule_Common" "true"
// DISABLE-ERRORS
import kotlin.Double as MyDouble
typealias MyInt = Int
actual class <caret>My {
actual fun foo(): MyInt = 42
actual val some: MyDouble = 4.0
}
@@ -0,0 +1,4 @@
expect class My(x: Int, y: Int) {
val x: Int
val y: Int
}
@@ -0,0 +1,4 @@
// "Create expected class in common module testModule_Common" "true"
// DISABLE-ERRORS
actual data class <caret>My actual constructor(actual val x: Int, actual val y: Int)
@@ -0,0 +1,4 @@
// "Create expected class in common module testModule_Common" "true"
// DISABLE-ERRORS
actual data class My actual constructor(actual val x: Int, actual val y: Int)
@@ -5,6 +5,4 @@ expect enum class My {
SECOND,
LAST;
val num: Int
}
@@ -0,0 +1,2 @@
// My: to be implemented
// DISABLE-ERRORS
@@ -0,0 +1,8 @@
// My: to be implemented
// DISABLE-ERRORS
expect enum class My {
;
fun getHello(): String
}
@@ -0,0 +1,8 @@
// "Create expected enum class in common module testModule_Common" "true"
// DISABLE-ERRORS
actual enum class <caret>My {
;
actual fun getHello() = "Hello"
}
@@ -0,0 +1,8 @@
// "Create expected enum class in common module testModule_Common" "true"
// DISABLE-ERRORS
actual enum class My {
;
actual fun getHello() = "Hello"
}
@@ -0,0 +1 @@
expect fun <T> createList(): java.util.ArrayList<T>
@@ -0,0 +1,6 @@
// "Create expected function in common module proj_Common" "true"
// DISABLE-ERRORS
import java.util.ArrayList
actual fun <T> <caret>createList(): ArrayList<T> = ArrayList()
@@ -0,0 +1,6 @@
// "Create expected function in common module proj_Common" "true"
// DISABLE-ERRORS
import java.util.ArrayList
actual fun <T> createList(): ArrayList<T> = ArrayList()
@@ -7,7 +7,10 @@ expect class My {
class OtherNested(d: Double) {
val dd: Double
var d: Double
}
val s: String
}
}
@@ -0,0 +1,2 @@
// My: to be implemented
// DISABLE-ERRORS
@@ -0,0 +1,21 @@
// My: to be implemented
// DISABLE-ERRORS
expect sealed class My private constructor(x: Double) {
abstract val num: Int
open fun isGood(): Boolean
object First : My {
override val num: Int
}
class Some(num: Int) : My {
override val num: Int
}
object Best : My {
override val num: Int
override fun isGood(): Boolean
}
val x: Double
}
@@ -0,0 +1,21 @@
// "Create expected class in common module testModule_Common" "true"
// DISABLE-ERRORS
actual sealed class <caret>My actual constructor(actual val x: Double) {
actual abstract val num: Int
actual open fun isGood() = false
actual object First : My(1.0) {
actual override val num = 0
}
actual class Some actual constructor(actual override val num: Int) : My(num.toDouble())
actual object Best : My(999.0) {
actual override val num = 42
actual override fun isGood() = true
}
}
@@ -0,0 +1,21 @@
// "Create expected class in common module testModule_Common" "true"
// DISABLE-ERRORS
actual sealed class My actual constructor(actual val x: Double) {
actual abstract val num: Int
actual open fun isGood() = false
actual object First : My(1.0) {
actual override val num = 0
}
actual class Some actual constructor(actual override val num: Int) : My(num.toDouble())
actual object Best : My(999.0) {
actual override val num = 42
actual override fun isGood() = true
}
}
@@ -0,0 +1 @@
// Not to be implemented
@@ -0,0 +1,9 @@
// "Create expected class in common module testModule_Common" "false"
// DISABLE-ERRORS
// ACTION: Remove 'actual' modifier
class Some(val x: Int, val y: Int) {
fun processIt(z: Int) = x + y - z
}
actual typealias <caret>My = Some
@@ -0,0 +1,4 @@
// My: to be implemented
// DISABLE-ERRORS
annotation class CommonAnnotation
@@ -0,0 +1,10 @@
// My: to be implemented
// DISABLE-ERRORS
annotation class CommonAnnotation
expect class My {
tailrec fun foo(arg: Int): Int
var some: Boolean
fun initialize()
}
@@ -0,0 +1,20 @@
// "Create expected class in common module testModule_Common" "true"
// DISABLE-ERRORS
annotation class PlatformAnnotation
actual class <caret>My {
@PlatformAnnotation
actual tailrec fun foo(arg: Int): Int {
if (arg <= 1) return 1
return foo(arg - 1)
}
// Here we will have an error (lateinit is not supported on both sides)
actual lateinit var some: Boolean
@CommonAnnotation
actual fun initialize() {
some = true
}
}
@@ -0,0 +1,20 @@
// "Create expected class in common module testModule_Common" "true"
// DISABLE-ERRORS
annotation class PlatformAnnotation
actual class <caret>My {
@PlatformAnnotation
actual tailrec fun foo(arg: Int): Int {
if (arg <= 1) return 1
return foo(arg - 1)
}
// Here we will have an error (lateinit is not supported on both sides)
actual lateinit var some: Boolean
@CommonAnnotation
actual fun initialize() {
some = true
}
}
@@ -264,6 +264,11 @@ public class QuickFixMultiModuleTestGenerated extends AbstractQuickFixMultiModul
runTest("idea/testData/multiModuleQuickFix/expectClass/");
}
@TestMetadata("expectClassCommented")
public void testExpectClassCommented() throws Exception {
runTest("idea/testData/multiModuleQuickFix/expectClassCommented/");
}
@TestMetadata("expectClassFunction")
public void testExpectClassFunction() throws Exception {
runTest("idea/testData/multiModuleQuickFix/expectClassFunction/");
@@ -279,16 +284,36 @@ public class QuickFixMultiModuleTestGenerated extends AbstractQuickFixMultiModul
runTest("idea/testData/multiModuleQuickFix/expectClassPropertyInConstructor/");
}
@TestMetadata("expectClassWithAliases")
public void testExpectClassWithAliases() throws Exception {
runTest("idea/testData/multiModuleQuickFix/expectClassWithAliases/");
}
@TestMetadata("expectClassWithSupertype")
public void testExpectClassWithSupertype() throws Exception {
runTest("idea/testData/multiModuleQuickFix/expectClassWithSupertype/");
}
@TestMetadata("expectDataClass")
public void testExpectDataClass() throws Exception {
runTest("idea/testData/multiModuleQuickFix/expectDataClass/");
}
@TestMetadata("expectEnum")
public void testExpectEnum() throws Exception {
runTest("idea/testData/multiModuleQuickFix/expectEnum/");
}
@TestMetadata("expectEnumEmpty")
public void testExpectEnumEmpty() throws Exception {
runTest("idea/testData/multiModuleQuickFix/expectEnumEmpty/");
}
@TestMetadata("expectFunWithJdk")
public void testExpectFunWithJdk() throws Exception {
runTest("idea/testData/multiModuleQuickFix/expectFunWithJdk/");
}
@TestMetadata("expectFunction")
public void testExpectFunction() throws Exception {
runTest("idea/testData/multiModuleQuickFix/expectFunction/");
@@ -304,6 +329,21 @@ public class QuickFixMultiModuleTestGenerated extends AbstractQuickFixMultiModul
runTest("idea/testData/multiModuleQuickFix/expectProperty/");
}
@TestMetadata("expectSealedClass")
public void testExpectSealedClass() throws Exception {
runTest("idea/testData/multiModuleQuickFix/expectSealedClass/");
}
@TestMetadata("expectTypeAlias")
public void testExpectTypeAlias() throws Exception {
runTest("idea/testData/multiModuleQuickFix/expectTypeAlias/");
}
@TestMetadata("expectWithAnnotations")
public void testExpectWithAnnotations() throws Exception {
runTest("idea/testData/multiModuleQuickFix/expectWithAnnotations/");
}
@TestMetadata("function")
public void testFunction() throws Exception {
runTest("idea/testData/multiModuleQuickFix/function/");