[FIR] Split creating fir for java declarations to separate methods

This commit is contained in:
Dmitriy Novozhilov
2020-09-24 12:26:25 +03:00
parent 0d29d6d361
commit a0db510e49
@@ -7,12 +7,10 @@ package org.jetbrains.kotlin.fir.java
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.builder.*
@@ -26,6 +24,7 @@ import org.jetbrains.kotlin.fir.resolve.providers.SymbolProviderCache
import org.jetbrains.kotlin.fir.resolve.scopes.wrapScopeWithJvmMapped
import org.jetbrains.kotlin.fir.symbols.CallableId
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
import org.jetbrains.kotlin.load.java.JavaClassFinder
@@ -57,7 +56,8 @@ class JavaSymbolProvider(
private val scopeProvider = JavaScopeProvider(::wrapScopeWithJvmMapped, this)
private val facade: KotlinJavaPsiFacade get() = KotlinJavaPsiFacade.getInstance(project)
private val parentClassTypeParameterStackCache: SymbolProviderCache<FirRegularClassSymbol, JavaTypeParameterStack> = SymbolProviderCache()
private val parentClassTypeParameterStackCache: SymbolProviderCache<FirRegularClassSymbol, JavaTypeParameterStack> =
SymbolProviderCache()
private fun findClass(
classId: ClassId,
@@ -65,7 +65,8 @@ class JavaSymbolProvider(
): JavaClass? = facade.findClass(JavaClassFinder.Request(classId, previouslyFoundClassFileContent = content?.content), searchScope)
@FirSymbolProviderInternals
override fun getTopLevelCallableSymbolsTo(destination: MutableList<FirCallableSymbol<*>>, packageFqName: FqName, name: Name) {}
override fun getTopLevelCallableSymbolsTo(destination: MutableList<FirCallableSymbol<*>>, packageFqName: FqName, name: Name) {
}
private fun JavaTypeParameter.toFirTypeParameterSymbol(
javaTypeParameterStack: JavaTypeParameterStack
@@ -140,254 +141,312 @@ class JavaSymbolProvider(
}
},
) { firSymbol, foundClass ->
foundClass?.let { javaClass ->
val javaTypeParameterStack = JavaTypeParameterStack()
val outerClassId = classId.outerClassId
val parentClassSymbol = if (outerClassId != null) {
getClassLikeSymbolByFqName(outerClassId)
} else null
if (parentClassSymbol != null) {
val parentStack = parentClassTypeParameterStackCache[parentClassSymbol]
?: (parentClassSymbol.fir as? FirJavaClass)?.javaTypeParameterStack
if (parentStack != null) {
javaTypeParameterStack.addStack(parentStack)
}
convertJavaClassToFir(firSymbol, foundClass)
}
}
private class ValueParametersForAnnotationConstructor {
val valueParameters: MutableList<FirJavaValueParameter> = mutableListOf()
var valueParameterForValue: FirJavaValueParameter? = null
}
private fun convertJavaClassToFir(classSymbol: FirRegularClassSymbol, javaClass: JavaClass?): FirJavaClass? {
if (javaClass == null) return null
val classId = classSymbol.classId
val javaTypeParameterStack = JavaTypeParameterStack()
val outerClassId = classId.outerClassId
val parentClassSymbol = if (outerClassId != null) {
getClassLikeSymbolByFqName(outerClassId)
} else null
if (parentClassSymbol != null) {
val parentStack = parentClassTypeParameterStackCache[parentClassSymbol]
?: (parentClassSymbol.fir as? FirJavaClass)?.javaTypeParameterStack
if (parentStack != null) {
javaTypeParameterStack.addStack(parentStack)
}
}
val firJavaClass = buildJavaClass {
source = (javaClass as? JavaElementImpl<*>)?.psi?.toFirPsiSourceElement()
session = this@JavaSymbolProvider.session
symbol = classSymbol
name = javaClass.name
visibility = javaClass.visibility
modality = javaClass.modality
classKind = javaClass.classKind
this.isTopLevel = outerClassId == null
isStatic = javaClass.isStatic
this.javaTypeParameterStack = javaTypeParameterStack
parentClassTypeParameterStackCache[classSymbol] = javaTypeParameterStack
existingNestedClassifierNames += javaClass.innerClassNames
scopeProvider = this@JavaSymbolProvider.scopeProvider
val classTypeParameters = javaClass.typeParameters.convertTypeParameters(javaTypeParameterStack)
typeParameters += classTypeParameters
if (!isStatic && parentClassSymbol != null) {
typeParameters += parentClassSymbol.fir.typeParameters.map {
buildOuterClassTypeParameterRef { symbol = it.symbol }
}
val firJavaClass = buildJavaClass {
source = (javaClass as? JavaElementImpl<*>)?.psi?.toFirPsiSourceElement()
session = this@JavaSymbolProvider.session
symbol = firSymbol
name = javaClass.name
visibility = javaClass.visibility
modality = javaClass.modality
classKind = javaClass.classKind
this.isTopLevel = outerClassId == null
isStatic = javaClass.isStatic
this.javaTypeParameterStack = javaTypeParameterStack
parentClassTypeParameterStackCache[firSymbol] = javaTypeParameterStack
existingNestedClassifierNames += javaClass.innerClassNames
scopeProvider = this@JavaSymbolProvider.scopeProvider
val classTypeParameters = foundClass.typeParameters.convertTypeParameters(javaTypeParameterStack)
typeParameters += classTypeParameters
if (!isStatic && parentClassSymbol != null) {
typeParameters += parentClassSymbol.fir.typeParameters.map {
buildOuterClassTypeParameterRef { symbol = it.symbol }
}
}
status = FirResolvedDeclarationStatusImpl(
javaClass.visibility,
javaClass.modality
).apply {
this.isInner = !isTopLevel && !this@buildJavaClass.isStatic
isCompanion = false
isData = false
isInline = false
isFun = classKind == ClassKind.INTERFACE
}
addAnnotationsFrom(this@JavaSymbolProvider.session, javaClass, javaTypeParameterStack)
// TODO: may be we can process fields & methods later.
// However, they should be built up to override resolve stage
for (javaField in javaClass.fields) {
val fieldName = javaField.name
val fieldId = CallableId(classId.packageFqName, classId.relativeClassName, fieldName)
val returnType = javaField.type
val firJavaDeclaration = when {
javaField.isEnumEntry -> buildEnumEntry {
source = (javaField as? JavaElementImpl<*>)?.psi?.toFirPsiSourceElement()
session = this@JavaSymbolProvider.session
symbol = FirVariableSymbol(fieldId)
name = fieldName
status = FirResolvedDeclarationStatusImpl(
javaField.visibility,
javaField.modality
).apply {
isStatic = javaField.isStatic
isExpect = false
isActual = false
isOverride = false
}
returnTypeRef = returnType.toFirJavaTypeRef(this@JavaSymbolProvider.session, javaTypeParameterStack)
resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES
origin = FirDeclarationOrigin.Java
addAnnotationsFrom(this@JavaSymbolProvider.session, javaField, javaTypeParameterStack)
}
else -> buildJavaField {
source = (javaField as? JavaElementImpl<*>)?.psi?.toFirPsiSourceElement()
session = this@JavaSymbolProvider.session
symbol = FirFieldSymbol(fieldId)
name = fieldName
status = FirResolvedDeclarationStatusImpl(
javaField.visibility,
javaField.modality
).apply {
isStatic = javaField.isStatic
isExpect = false
isActual = false
isOverride = false
}
visibility = javaField.visibility
modality = javaField.modality
returnTypeRef = returnType.toFirJavaTypeRef(this@JavaSymbolProvider.session, javaTypeParameterStack)
isVar = !javaField.isFinal
isStatic = javaField.isStatic
addAnnotationsFrom(this@JavaSymbolProvider.session, javaField, javaTypeParameterStack)
}
}
declarations += firJavaDeclaration
}
val valueParametersForAnnotationConstructor = mutableListOf<FirJavaValueParameter>()
var valueParameterForValueInAnnotationConstructor: FirJavaValueParameter? = null
val classIsAnnotation = classKind == ClassKind.ANNOTATION_CLASS
}
status = FirResolvedDeclarationStatusImpl(
javaClass.visibility,
javaClass.modality
).apply {
this.isInner = !isTopLevel && !this@buildJavaClass.isStatic
isCompanion = false
isData = false
isInline = false
isFun = classKind == ClassKind.INTERFACE
}
addAnnotationsFrom(this@JavaSymbolProvider.session, javaClass, javaTypeParameterStack)
// TODO: may be we can process fields & methods later.
// However, they should be built up to override resolve stage
for (javaField in javaClass.fields) {
declarations += convertJavaFieldToFir(javaField, classId, javaTypeParameterStack)
}
val valueParametersForAnnotationConstructor = ValueParametersForAnnotationConstructor()
val classIsAnnotation = classKind == ClassKind.ANNOTATION_CLASS
for (javaMethod in javaClass.methods) {
val methodName = javaMethod.name
val methodId = CallableId(classId.packageFqName, classId.relativeClassName, methodName)
val methodSymbol = FirNamedFunctionSymbol(methodId)
val returnType = javaMethod.returnType
val firJavaMethod = buildJavaMethod {
session = this@JavaSymbolProvider.session
source = (javaMethod as? JavaElementImpl<*>)?.psi?.toFirPsiSourceElement()
symbol = methodSymbol
name = methodName
visibility = javaMethod.visibility
modality = javaMethod.modality
returnTypeRef = returnType.toFirJavaTypeRef(this@JavaSymbolProvider.session, javaTypeParameterStack)
isStatic = javaMethod.isStatic
typeParameters += javaMethod.typeParameters.convertTypeParameters(javaTypeParameterStack)
addAnnotationsFrom(this@JavaSymbolProvider.session, javaMethod, javaTypeParameterStack)
for ((index, valueParameter) in javaMethod.valueParameters.withIndex()) {
valueParameters += valueParameter.toFirValueParameter(
this@JavaSymbolProvider.session, index, javaTypeParameterStack,
)
}
status = FirResolvedDeclarationStatusImpl(
javaMethod.visibility,
javaMethod.modality
).apply {
isStatic = javaMethod.isStatic
isExpect = false
isActual = false
isOverride = false
// Approximation: all Java methods with name that allows to use it in operator form are considered operators
// We need here more detailed checks (see modifierChecks.kt)
isOperator = name in ALL_JAVA_OPERATION_NAMES || OperatorNameConventions.COMPONENT_REGEX.matches(name.asString())
isInfix = false
isInline = false
isTailRec = false
isExternal = false
isSuspend = false
}
}
if (classIsAnnotation) {
val parameterForAnnotationConstructor = buildJavaValueParameter {
session = this@JavaSymbolProvider.session
returnTypeRef = firJavaMethod.returnTypeRef
name = firJavaMethod.name
if (javaMethod.hasAnnotationParameterDefaultValue) {
defaultValue = buildExpressionStub()
}
isVararg = javaMethod.returnType is JavaArrayType
}
if (firJavaMethod.name == VALUE_METHOD_NAME) {
valueParameterForValueInAnnotationConstructor = parameterForAnnotationConstructor
} else {
valueParametersForAnnotationConstructor += parameterForAnnotationConstructor
}
}
declarations += firJavaMethod
}
val javaClassDeclaredConstructors = javaClass.constructors
val constructorId = CallableId(classId.packageFqName, classId.relativeClassName, classId.shortClassName)
fun buildSelfTypeRef() = firSymbol.constructType(
this@buildJavaClass.typeParameters.map {
ConeTypeParameterTypeImpl(it.symbol.toLookupTag(), false)
}.toTypedArray(),
false,
)
fun prepareJavaConstructor(
visibility: Visibility = this.visibility,
psi: PsiElement? = null,
isPrimary: Boolean = false,
): FirJavaConstructorBuilder {
val constructorSymbol = FirConstructorSymbol(constructorId)
return FirJavaConstructorBuilder().apply {
source = psi?.toFirPsiSourceElement()
session = this@JavaSymbolProvider.session
symbol = constructorSymbol
isInner = javaClass.outerClass != null && !javaClass.isStatic
val isThisInner = this.isInner
status = FirResolvedDeclarationStatusImpl(
visibility,
Modality.FINAL
).apply {
isExpect = false
isActual = false
isOverride = false
isInner = isThisInner
}
this.visibility = visibility
this.isPrimary = isPrimary
returnTypeRef = buildResolvedTypeRef {
type = buildSelfTypeRef()
}
typeParameters += classTypeParameters.map { buildConstructedClassTypeParameterRef { symbol = it.symbol } }
}
}
if (javaClassDeclaredConstructors.isEmpty()
&& javaClass.classKind == ClassKind.CLASS
&& javaClass.hasDefaultConstructor()) {
declarations += prepareJavaConstructor(isPrimary = true).build()
}
for (javaConstructor in javaClassDeclaredConstructors) {
declarations += prepareJavaConstructor(
visibility = javaConstructor.visibility,
psi = (javaConstructor as? JavaElementImpl<*>)?.psi,
).apply {
this.typeParameters += javaConstructor.typeParameters.convertTypeParameters(javaTypeParameterStack)
addAnnotationsFrom(this@JavaSymbolProvider.session, javaConstructor, javaTypeParameterStack)
for ((index, valueParameter) in javaConstructor.valueParameters.withIndex()) {
valueParameters += valueParameter.toFirValueParameter(
this@JavaSymbolProvider.session, index, javaTypeParameterStack,
)
}
}.build()
}
if (classKind == ClassKind.ENUM_CLASS) {
generateValuesFunction(session, classId.packageFqName, classId.relativeClassName)
generateValueOfFunction(session, classId.packageFqName, classId.relativeClassName)
}
if (classIsAnnotation) {
declarations += buildJavaConstructor {
session = this@JavaSymbolProvider.session
symbol = FirConstructorSymbol(constructorId)
status = FirResolvedDeclarationStatusImpl(Visibilities.Public, Modality.FINAL)
returnTypeRef = buildResolvedTypeRef {
type = buildSelfTypeRef()
}
valueParameters.addIfNotNull(valueParameterForValueInAnnotationConstructor)
valueParameters += valueParametersForAnnotationConstructor
visibility = Visibilities.Public
isInner = false
isPrimary = true
}
}
parentClassTypeParameterStackCache.remove(firSymbol)
}
firJavaClass.replaceSuperTypeRefs(
javaClass.supertypes.map { supertype ->
supertype.toFirResolvedTypeRef(
this@JavaSymbolProvider.session, javaTypeParameterStack, isForSupertypes = true, forTypeParameterBounds = false
)
}
for (javaMethod in javaClass.methods) {
declarations += convertJavaMethodToFir(
javaMethod,
classId,
javaTypeParameterStack,
classIsAnnotation,
valueParametersForAnnotationConstructor
)
}
val javaClassDeclaredConstructors = javaClass.constructors
val constructorId = CallableId(classId.packageFqName, classId.relativeClassName, classId.shortClassName)
if (javaClassDeclaredConstructors.isEmpty()
&& javaClass.classKind == ClassKind.CLASS
&& javaClass.hasDefaultConstructor()
) {
declarations += convertJavaConstructorToFir(
javaConstructor = null,
constructorId,
javaClass,
ownerClassBuilder = this,
classTypeParameters,
javaTypeParameterStack
)
}
for (javaConstructor in javaClassDeclaredConstructors) {
declarations += convertJavaConstructorToFir(
javaConstructor,
constructorId,
javaClass,
ownerClassBuilder = this,
classTypeParameters,
javaTypeParameterStack,
)
}
if (classKind == ClassKind.ENUM_CLASS) {
generateValuesFunction(session, classId.packageFqName, classId.relativeClassName)
generateValueOfFunction(session, classId.packageFqName, classId.relativeClassName)
}
if (classIsAnnotation) {
declarations += buildConstructorForAnnotationClass(constructorId, this, valueParametersForAnnotationConstructor)
}
parentClassTypeParameterStackCache.remove(classSymbol)
}
firJavaClass.replaceSuperTypeRefs(
javaClass.supertypes.map { supertype ->
supertype.toFirResolvedTypeRef(
this@JavaSymbolProvider.session, javaTypeParameterStack, isForSupertypes = true, forTypeParameterBounds = false
)
}
)
return firJavaClass
}
private fun convertJavaFieldToFir(
javaField: JavaField,
classId: ClassId,
javaTypeParameterStack: JavaTypeParameterStack
): FirDeclaration {
val fieldName = javaField.name
val fieldId = CallableId(classId.packageFqName, classId.relativeClassName, fieldName)
val returnType = javaField.type
return when {
javaField.isEnumEntry -> buildEnumEntry {
source = (javaField as? JavaElementImpl<*>)?.psi?.toFirPsiSourceElement()
session = this@JavaSymbolProvider.session
symbol = FirVariableSymbol(fieldId)
name = fieldName
status = FirResolvedDeclarationStatusImpl(
javaField.visibility,
javaField.modality
).apply {
isStatic = javaField.isStatic
isExpect = false
isActual = false
isOverride = false
}
returnTypeRef = returnType.toFirJavaTypeRef(this@JavaSymbolProvider.session, javaTypeParameterStack)
resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES
origin = FirDeclarationOrigin.Java
addAnnotationsFrom(this@JavaSymbolProvider.session, javaField, javaTypeParameterStack)
}
else -> buildJavaField {
source = (javaField as? JavaElementImpl<*>)?.psi?.toFirPsiSourceElement()
session = this@JavaSymbolProvider.session
symbol = FirFieldSymbol(fieldId)
name = fieldName
status = FirResolvedDeclarationStatusImpl(
javaField.visibility,
javaField.modality
).apply {
isStatic = javaField.isStatic
isExpect = false
isActual = false
isOverride = false
}
visibility = javaField.visibility
modality = javaField.modality
returnTypeRef = returnType.toFirJavaTypeRef(this@JavaSymbolProvider.session, javaTypeParameterStack)
isVar = !javaField.isFinal
isStatic = javaField.isStatic
addAnnotationsFrom(this@JavaSymbolProvider.session, javaField, javaTypeParameterStack)
}
}
}
private fun convertJavaMethodToFir(
javaMethod: JavaMethod,
classId: ClassId,
javaTypeParameterStack: JavaTypeParameterStack,
classIsAnnotation: Boolean,
valueParametersForAnnotationConstructor: ValueParametersForAnnotationConstructor
): FirJavaMethod {
val methodName = javaMethod.name
val methodId = CallableId(classId.packageFqName, classId.relativeClassName, methodName)
val methodSymbol = FirNamedFunctionSymbol(methodId)
val returnType = javaMethod.returnType
val firJavaMethod = buildJavaMethod {
session = this@JavaSymbolProvider.session
source = (javaMethod as? JavaElementImpl<*>)?.psi?.toFirPsiSourceElement()
symbol = methodSymbol
name = methodName
visibility = javaMethod.visibility
modality = javaMethod.modality
returnTypeRef = returnType.toFirJavaTypeRef(this@JavaSymbolProvider.session, javaTypeParameterStack)
isStatic = javaMethod.isStatic
typeParameters += javaMethod.typeParameters.convertTypeParameters(javaTypeParameterStack)
addAnnotationsFrom(this@JavaSymbolProvider.session, javaMethod, javaTypeParameterStack)
for ((index, valueParameter) in javaMethod.valueParameters.withIndex()) {
valueParameters += valueParameter.toFirValueParameter(
this@JavaSymbolProvider.session, index, javaTypeParameterStack,
)
}
status = FirResolvedDeclarationStatusImpl(
javaMethod.visibility,
javaMethod.modality
).apply {
isStatic = javaMethod.isStatic
isExpect = false
isActual = false
isOverride = false
// Approximation: all Java methods with name that allows to use it in operator form are considered operators
// We need here more detailed checks (see modifierChecks.kt)
isOperator = name in ALL_JAVA_OPERATION_NAMES || OperatorNameConventions.COMPONENT_REGEX.matches(name.asString())
isInfix = false
isInline = false
isTailRec = false
isExternal = false
isSuspend = false
}
}
if (classIsAnnotation) {
val parameterForAnnotationConstructor = buildJavaValueParameter {
session = this@JavaSymbolProvider.session
returnTypeRef = firJavaMethod.returnTypeRef
name = firJavaMethod.name
if (javaMethod.hasAnnotationParameterDefaultValue) {
defaultValue = buildExpressionStub()
}
isVararg = javaMethod.returnType is JavaArrayType
}
if (firJavaMethod.name == VALUE_METHOD_NAME) {
valueParametersForAnnotationConstructor.valueParameterForValue = parameterForAnnotationConstructor
} else {
valueParametersForAnnotationConstructor.valueParameters += parameterForAnnotationConstructor
}
}
return firJavaMethod
}
private fun convertJavaConstructorToFir(
javaConstructor: JavaConstructor?,
constructorId: CallableId,
javaClass: JavaClass,
ownerClassBuilder: FirJavaClassBuilder,
classTypeParameters: List<FirTypeParameter>,
javaTypeParameterStack: JavaTypeParameterStack,
): FirJavaConstructor {
val constructorSymbol = FirConstructorSymbol(constructorId)
return buildJavaConstructor {
source = (javaConstructor as? JavaElementImpl<*>)?.psi?.toFirPsiSourceElement()
session = this@JavaSymbolProvider.session
symbol = constructorSymbol
isInner = javaClass.outerClass != null && !javaClass.isStatic
val isThisInner = this.isInner
val visibility = javaConstructor?.visibility ?: ownerClassBuilder.visibility
status = FirResolvedDeclarationStatusImpl(
visibility,
Modality.FINAL
).apply {
isExpect = false
isActual = false
isOverride = false
isInner = isThisInner
}
this.visibility = visibility
isPrimary = javaConstructor == null
returnTypeRef = buildResolvedTypeRef {
type = ownerClassBuilder.buildSelfTypeRef()
}
typeParameters += classTypeParameters.map { buildConstructedClassTypeParameterRef { symbol = it.symbol } }
if (javaConstructor != null) {
this.typeParameters += javaConstructor.typeParameters.convertTypeParameters(javaTypeParameterStack)
addAnnotationsFrom(this@JavaSymbolProvider.session, javaConstructor, javaTypeParameterStack)
for ((index, valueParameter) in javaConstructor.valueParameters.withIndex()) {
valueParameters += valueParameter.toFirValueParameter(
this@JavaSymbolProvider.session, index, javaTypeParameterStack,
)
}
}
}
}
private fun buildConstructorForAnnotationClass(
constructorId: CallableId,
ownerClassBuilder: FirJavaClassBuilder,
valueParametersForAnnotationConstructor: ValueParametersForAnnotationConstructor
): FirJavaConstructor {
return buildJavaConstructor {
session = this@JavaSymbolProvider.session
symbol = FirConstructorSymbol(constructorId)
status = FirResolvedDeclarationStatusImpl(Visibilities.Public, Modality.FINAL)
returnTypeRef = buildResolvedTypeRef {
type = ownerClassBuilder.buildSelfTypeRef()
}
valueParameters.addIfNotNull(valueParametersForAnnotationConstructor.valueParameterForValue)
valueParameters += valueParametersForAnnotationConstructor.valueParameters
visibility = Visibilities.Public
isInner = false
isPrimary = true
}
}
private fun FirJavaClassBuilder.buildSelfTypeRef(): ConeKotlinType = symbol.constructType(
typeParameters.map {
ConeTypeParameterTypeImpl(it.symbol.toLookupTag(), isNullable = false)
}.toTypedArray(),
isNullable = false,
)
override fun getPackage(fqName: FqName): FqName? {
return packageCache.lookupCacheOrCalculate(fqName) {
try {