FIR LC: populate no-arg constructor if needed

This commit is contained in:
Jinseong Jeon
2021-11-22 22:31:33 -08:00
committed by Ilya Kirillov
parent d253f9ba0e
commit edc17a9d5f
5 changed files with 136 additions and 20 deletions
@@ -19,16 +19,16 @@ import org.jetbrains.kotlin.psi.KtPropertyAccessor
import org.jetbrains.kotlin.psi.KtSecondaryConstructor
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
open class FirLightIdentifier(
internal class FirLightIdentifier(
private val lightOwner: PsiElement,
private val firSymbol: KtSymbol
private val firSymbol: KtSymbol?
) : LightIdentifier(
lightOwner.manager,
(firSymbol as? KtNamedSymbol)?.name?.identifierOrNullIfSpecial
), PsiCompiledElement, PsiElementWithOrigin<PsiElement> {
override val origin: PsiElement?
get() = when (val ktDeclaration = firSymbol.psi) {
get() = when (val ktDeclaration = firSymbol?.psi) {
is KtSecondaryConstructor -> ktDeclaration.getConstructorKeyword()
is KtPrimaryConstructor -> ktDeclaration.getConstructorKeyword()
?: ktDeclaration.valueParameterList
@@ -44,4 +44,4 @@ open class FirLightIdentifier(
override fun getContainingFile(): PsiFile = lightOwner.containingFile
override fun getTextRange(): TextRange = origin?.textRange ?: TextRange.EMPTY_RANGE
override fun getTextOffset(): Int = origin?.textOffset ?: -1
}
}
@@ -6,19 +6,11 @@
package org.jetbrains.kotlin.light.classes.symbol.classes
import com.intellij.psi.PsiManager
import com.intellij.psi.PsiModifier
import com.intellij.psi.PsiReferenceList
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import org.jetbrains.kotlin.analysis.providers.createProjectWideOutOfBlockModificationTracker
import org.jetbrains.kotlin.asJava.builder.LightMemberOriginForDeclaration
import org.jetbrains.kotlin.asJava.classes.KotlinSuperTypeListBuilder
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.classes.METHOD_INDEX_BASE
import org.jetbrains.kotlin.asJava.classes.shouldNotBeVisibleAsLightClass
import org.jetbrains.kotlin.asJava.elements.KtLightField
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.analysis.api.symbols.*
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithMembers
import org.jetbrains.kotlin.analysis.api.symbols.markers.isPrivateOrPrivateToThis
@@ -26,10 +18,16 @@ import org.jetbrains.kotlin.analysis.api.tokens.HackToForceAllowRunningAnalyzeOn
import org.jetbrains.kotlin.analysis.api.tokens.hackyAllowRunningOnEdt
import org.jetbrains.kotlin.analysis.api.types.KtNonErrorClassType
import org.jetbrains.kotlin.analysis.api.types.KtType
import org.jetbrains.kotlin.asJava.builder.LightMemberOriginForDeclaration
import org.jetbrains.kotlin.asJava.classes.*
import org.jetbrains.kotlin.asJava.elements.KtLightField
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.lexer.KtTokens.*
import org.jetbrains.kotlin.light.classes.symbol.*
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClass
@@ -114,17 +112,68 @@ internal fun FirLightClassBase.createConstructors(
declarations: Sequence<KtConstructorSymbol>,
result: MutableList<KtLightMethod>
) {
for (declaration in declarations) {
if (declaration.isHiddenOrSynthetic(project)) continue
val constructors = declarations.toList()
if (constructors.isEmpty()) {
result.add(defaultConstructor())
return
}
for (constructor in constructors) {
if (constructor.isHiddenOrSynthetic(project)) continue
result.add(
FirLightConstructorForSymbol(
constructorSymbol = declaration,
constructorSymbol = constructor,
lightMemberOrigin = null,
containingClass = this@createConstructors,
methodIndex = METHOD_INDEX_BASE
)
)
}
val primaryConstructor = constructors.singleOrNull { it.isPrimary }
if (primaryConstructor != null && shouldGenerateNoArgOverload(primaryConstructor, constructors)) {
result.add(
noArgConstructor(primaryConstructor.visibility.externalDisplayName, METHOD_INDEX_FOR_NO_ARG_OVERLOAD_CTOR)
)
}
}
private fun FirLightClassBase.shouldGenerateNoArgOverload(
primaryConstructor: KtConstructorSymbol,
constructors: Iterable<KtConstructorSymbol>,
): Boolean {
val classOrObject = kotlinOrigin ?: return false
return !primaryConstructor.visibility.isPrivateOrPrivateToThis() &&
!classOrObject.hasModifier(INNER_KEYWORD) && !isEnum &&
!classOrObject.hasModifier(SEALED_KEYWORD) &&
primaryConstructor.valueParameters.isNotEmpty() &&
primaryConstructor.valueParameters.all { it.hasDefaultValue } &&
constructors.none { it.valueParameters.isEmpty() } &&
!primaryConstructor.hasJvmOverloadsAnnotation()
}
private fun FirLightClassBase.defaultConstructor(): KtLightMethod {
val classOrObject = kotlinOrigin
val visibility = when {
classOrObject is KtObjectDeclaration || classOrObject?.hasModifier(SEALED_KEYWORD) == true || isEnum -> PsiModifier.PRIVATE
classOrObject is KtEnumEntry -> PsiModifier.PACKAGE_LOCAL
else -> PsiModifier.PUBLIC
}
return noArgConstructor(visibility, METHOD_INDEX_FOR_DEFAULT_CTOR)
}
private fun FirLightClassBase.noArgConstructor(
visibility: String,
methodIndex: Int,
): KtLightMethod {
return FirLightNoArgConstructor(
LightMemberOriginForDeclaration(
originalElement = kotlinOrigin!!,
originKind = JvmDeclarationOriginKind.OTHER,
auxiliaryOriginalElement = kotlinOrigin as? KtDeclaration
),
this,
visibility,
methodIndex
)
}
internal fun FirLightClassBase.createMethods(
@@ -0,0 +1,63 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.light.classes.symbol
import com.intellij.psi.*
import org.jetbrains.kotlin.asJava.builder.LightMemberOrigin
import org.jetbrains.kotlin.asJava.classes.lazyPub
internal class FirLightNoArgConstructor(
lightMemberOrigin: LightMemberOrigin?,
containingClass: FirLightClassBase,
visibility: String,
methodIndex: Int,
): FirLightMethod(lightMemberOrigin, containingClass, methodIndex) {
private val _name: String? = containingClass.name
override fun getName(): String = _name ?: ""
override fun isConstructor(): Boolean = true
override fun hasTypeParameters(): Boolean = false
override fun getTypeParameterList(): PsiTypeParameterList? = null
override fun getTypeParameters(): Array<PsiTypeParameter> = PsiTypeParameter.EMPTY_ARRAY
private val _identifier: PsiIdentifier by lazyPub {
FirLightIdentifier(this, firSymbol = null)
}
override fun getNameIdentifier(): PsiIdentifier = _identifier
override fun isDeprecated(): Boolean = false
private val _modifiers: Set<String> by lazyPub {
setOf(visibility)
}
private val _modifierList: PsiModifierList by lazyPub {
FirLightMemberModifierList(this, _modifiers, emptyList())
}
override fun getModifierList(): PsiModifierList = _modifierList
private val _parameterList: PsiParameterList by lazyPub {
FirLightParameterList(this, callableSymbol = null) {}
}
override fun getParameterList(): PsiParameterList = _parameterList
override fun getReturnType(): PsiType? = null
override fun equals(other: Any?): Boolean =
this === other ||
(other is FirLightNoArgConstructor &&
kotlinOrigin == other.kotlinOrigin &&
containingClass == other.containingClass)
override fun hashCode(): Int = containingClass.hashCode()
override fun isValid(): Boolean = super.isValid() && containingClass.isValid
}
@@ -17,7 +17,7 @@ import org.jetbrains.kotlin.psi.KtParameterList
internal class FirLightParameterList(
private val parent: FirLightMethod,
private val callableSymbol: KtCallableSymbol,
private val callableSymbol: KtCallableSymbol?,
parameterPopulator: (LightParameterListBuilder) -> Unit,
) : KtLightElement<KtParameterList, PsiParameterList>,
// With this, a parent chain is properly built: from FirLightParameter through FirLightParameterList to FirLightMethod
@@ -31,8 +31,10 @@ internal class FirLightParameterList(
override val clsDelegate: PsiParameterList by lazyPub {
val builder = LightParameterListBuilder(manager, language)
FirLightParameterForReceiver.tryGet(callableSymbol, parent)?.let {
builder.addParameter(it)
callableSymbol?.let {
FirLightParameterForReceiver.tryGet(it, parent)?.let { receiver ->
builder.addParameter(receiver)
}
}
parameterPopulator.invoke(builder)
@@ -3,3 +3,5 @@ data class User(val name: String = "", val age: Int = 0)
data class Person(val name: String) {
var age: Int = 0
}
// FIR_COMPARISON