FIR: introduce FIR importing scopes #KT-24096 Fixed

This commit is contained in:
Simon Ogorodnik
2018-03-20 01:26:28 +03:00
committed by Mikhail Glukhikh
parent e6cd07b46e
commit 8933c9035b
26 changed files with 367 additions and 74 deletions
@@ -13,6 +13,10 @@ import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedClassImpl
import org.jetbrains.kotlin.fir.descriptors.ConeClassifierDescriptor
import org.jetbrains.kotlin.fir.descriptors.ConeTypeParameterDescriptor
import org.jetbrains.kotlin.fir.resolve.FirTypeResolver
import org.jetbrains.kotlin.fir.scopes.FirImportingScope
import org.jetbrains.kotlin.fir.scopes.impl.FirCompositeImportingScope
import org.jetbrains.kotlin.fir.scopes.impl.FirExplicitImportingScope
import org.jetbrains.kotlin.fir.scopes.impl.FirSelfImportingScope
import org.jetbrains.kotlin.fir.types.FirResolvedType
import org.jetbrains.kotlin.fir.types.FirType
import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeImpl
@@ -29,15 +33,27 @@ class FirClassifierResolveTransformer : FirTransformer<Nothing?>() {
lateinit var packageFqName: FqNameUnsafe
lateinit var importingScope: FirImportingScope
override fun transformFile(file: FirFile, data: Nothing?): CompositeTransformResult<FirFile> {
packageFqName = file.packageFqName.toUnsafe()
importingScope = FirCompositeImportingScope(
FirExplicitImportingScope(file.imports),
FirSelfImportingScope(file.packageFqName.toUnsafe(), file.session)
)
return file.also { it.acceptChildren(this, null) }.compose()
}
// TODO: Extract to separate transformer?
override fun transformType(type: FirType, data: Nothing?): CompositeTransformResult<FirType> {
val typeResolver = FirTypeResolver.getInstance(type.session)
return FirResolvedTypeImpl(type.session, type.psi, typeResolver.resolveType(type), false, type.annotations).compose()
return FirResolvedTypeImpl(
type.session,
type.psi,
typeResolver.resolveType(type, importingScope),
false,
type.annotations
).compose()
}
override fun transformResolvedType(resolvedType: FirResolvedType, data: Nothing?): CompositeTransformResult<FirType> {
@@ -6,14 +6,19 @@
package org.jetbrains.kotlin.fir.resolve
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.UnambiguousFqName
import org.jetbrains.kotlin.fir.service
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.FirQualifierPart
import org.jetbrains.kotlin.name.FqName
interface FirQualifierResolver {
fun resolveTypeWithPrefix(parts: List<FirQualifierPart>, prefix: UnambiguousFqName): ConeKotlinType?
fun resolveType(parts: List<FirQualifierPart>): ConeKotlinType?
fun resolveImport(fqName: FqName): UnambiguousFqName?
companion object {
fun getInstance(session: FirSession): FirQualifierResolver = session.service()
}
@@ -1,61 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.fir.resolve
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.UnambiguousFqName
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.ConeClassTypeImpl
import org.jetbrains.kotlin.fir.types.impl.ConeKotlinTypeProjectionInImpl
import org.jetbrains.kotlin.fir.types.impl.ConeKotlinTypeProjectionOutImpl
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.types.Variance
class FirQualifierResolverImpl(val session: FirSession) : FirQualifierResolver {
override fun resolveType(parts: List<FirQualifierPart>): ConeKotlinType? {
val firProvider = FirProvider.getInstance(session)
if (parts.isNotEmpty()) {
val lastPart = mutableListOf<FirQualifierPart>()
val firstPart = parts.toMutableList()
while (firstPart.isNotEmpty()) {
lastPart.add(0, firstPart.last())
firstPart.removeAt(firstPart.lastIndex)
val fqName = UnambiguousFqName(firstPart.toFqNameUnsafe(), lastPart.toFqName())
val foundClassifier = firProvider.getFirClassifierByFqName(fqName)
if (foundClassifier != null) {
return ConeClassTypeImpl(fqName, parts.flatMap {
it.typeArguments.map {
when (it) {
is FirStarProjection -> StarProjection
is FirTypeProjectionWithVariance -> {
val type = (it.type as FirResolvedType).type
when (it.variance) {
Variance.INVARIANT -> type
Variance.IN_VARIANCE -> ConeKotlinTypeProjectionInImpl(type)
Variance.OUT_VARIANCE -> ConeKotlinTypeProjectionOutImpl(type)
}
}
else -> error("!")
}
}
})
}
}
return null
} else {
return null
}
}
private fun List<FirQualifierPart>.toFqNameUnsafe() = toFqName().toUnsafe()
private fun List<FirQualifierPart>.toFqName() = fold(FqName.ROOT) { a, b -> a.child(b.name) }
}
@@ -6,13 +6,14 @@
package org.jetbrains.kotlin.fir.resolve
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.scopes.FirImportingScope
import org.jetbrains.kotlin.fir.service
import org.jetbrains.kotlin.fir.types.FirType
import org.jetbrains.kotlin.fir.types.ConeKotlinType
interface FirTypeResolver {
fun resolveType(type: FirType): ConeKotlinType
fun resolveType(type: FirType, importingScope: FirImportingScope): ConeKotlinType
companion object {
fun getInstance(session: FirSession): FirTypeResolver = session.service()
@@ -0,0 +1,103 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.fir.resolve.impl
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.UnambiguousFqName
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
import org.jetbrains.kotlin.fir.resolve.FirProvider
import org.jetbrains.kotlin.fir.resolve.FirQualifierResolver
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.ConeClassTypeImpl
import org.jetbrains.kotlin.fir.types.impl.ConeKotlinTypeProjectionInImpl
import org.jetbrains.kotlin.fir.types.impl.ConeKotlinTypeProjectionOutImpl
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.types.Variance
class FirQualifierResolverImpl(val session: FirSession) : FirQualifierResolver {
private fun FirMemberDeclaration.toConeKotlinType(fqName: UnambiguousFqName, parts: List<FirQualifierPart>): ConeKotlinType? {
return ConeClassTypeImpl(fqName, parts.flatMap {
it.typeArguments.map {
when (it) {
is FirStarProjection -> StarProjection
is FirTypeProjectionWithVariance -> {
val type = (it.type as FirResolvedType).type
when (it.variance) {
Variance.INVARIANT -> type
Variance.IN_VARIANCE -> ConeKotlinTypeProjectionInImpl(type)
Variance.OUT_VARIANCE -> ConeKotlinTypeProjectionOutImpl(type)
}
}
else -> error("!")
}
}
})
}
override fun resolveTypeWithPrefix(parts: List<FirQualifierPart>, prefix: UnambiguousFqName): ConeKotlinType? {
val firProvider = FirProvider.getInstance(session)
val fqName = UnambiguousFqName(prefix.packageFqName, parts.fold(prefix.classFqName) { prefix, suffix -> prefix.child(suffix.name) })
val foundClassifier = firProvider.getFirClassifierByFqName(fqName)
return foundClassifier?.toConeKotlinType(fqName, parts)
}
override fun resolveType(parts: List<FirQualifierPart>): ConeKotlinType? {
val firProvider = FirProvider.getInstance(session)
if (parts.isNotEmpty()) {
val lastPart = mutableListOf<FirQualifierPart>()
val firstPart = parts.toMutableList()
while (firstPart.isNotEmpty()) {
lastPart.add(0, firstPart.last())
firstPart.removeAt(firstPart.lastIndex)
val fqName = UnambiguousFqName(firstPart.toFqNameUnsafe(), lastPart.toFqName())
val foundClassifier = firProvider.getFirClassifierByFqName(fqName)
if (foundClassifier != null) {
return foundClassifier.toConeKotlinType(fqName, parts)
}
}
return null
} else {
return null
}
}
// TODO: extract common part from resolveType/Import
override fun resolveImport(fqName: FqName): UnambiguousFqName? {
val firProvider = FirProvider.getInstance(session)
if (!fqName.isRoot) {
val lastPart = mutableListOf<String>()
var firstPart = fqName
while (!firstPart.isRoot) {
lastPart.add(0, firstPart.shortName().asString())
firstPart = firstPart.parent()
val fqName = UnambiguousFqName(firstPart.toUnsafe(), FqName.fromSegments(lastPart))
val foundClassifier = firProvider.getFirClassifierByFqName(fqName)
if (foundClassifier != null) {
return fqName
}
}
return null
} else {
return null
}
}
private fun List<FirQualifierPart>.toFqNameUnsafe() = toFqName().toUnsafe()
private fun List<FirQualifierPart>.toFqName() = fold(FqName.ROOT) { a, b -> a.child(b.name) }
}
@@ -7,17 +7,26 @@ package org.jetbrains.kotlin.fir.resolve.impl
import org.jetbrains.kotlin.fir.resolve.FirQualifierResolver
import org.jetbrains.kotlin.fir.resolve.FirTypeResolver
import org.jetbrains.kotlin.fir.scopes.FirImportingScope
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.ConeKotlinErrorType
class FirTypeResolverImpl : FirTypeResolver {
override fun resolveType(type: FirType): ConeKotlinType {
override fun resolveType(type: FirType, importingScope: FirImportingScope): ConeKotlinType {
return when (type) {
is FirResolvedType -> type.type
is FirUserType -> {
val qualifierResolver = FirQualifierResolver.getInstance(type.session)
var resolvedType: ConeKotlinType? = null
importingScope.processClassifiersByName(type.qualifier.first().name) { fqName ->
resolvedType = qualifierResolver.resolveTypeWithPrefix(type.qualifier.drop(1), fqName)
resolvedType == null
}
// TODO: Imports
qualifierResolver.resolveType(type.qualifier) ?: ConeKotlinErrorType("Failed to resolve qualified type")
resolvedType ?: qualifierResolver.resolveType(type.qualifier) ?: ConeKotlinErrorType("Failed to resolve qualified type")
}
is FirErrorType -> {
ConeKotlinErrorType(type.reason)
@@ -0,0 +1,13 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.fir.scopes
import org.jetbrains.kotlin.fir.UnambiguousFqName
import org.jetbrains.kotlin.name.Name
interface FirImportingScope {
fun processClassifiersByName(name: Name, processor: (UnambiguousFqName) -> Boolean): Boolean
}
@@ -0,0 +1,21 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.fir.scopes.impl
import org.jetbrains.kotlin.fir.UnambiguousFqName
import org.jetbrains.kotlin.fir.scopes.FirImportingScope
import org.jetbrains.kotlin.name.Name
class FirCompositeImportingScope(vararg val scopes: FirImportingScope) : FirImportingScope {
override fun processClassifiersByName(name: Name, processor: (UnambiguousFqName) -> Boolean): Boolean {
for (scope in scopes) {
if (!scope.processClassifiersByName(name, processor)) {
return false
}
}
return true
}
}
@@ -0,0 +1,38 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.fir.scopes.impl
import org.jetbrains.kotlin.fir.UnambiguousFqName
import org.jetbrains.kotlin.fir.declarations.FirImport
import org.jetbrains.kotlin.fir.resolve.FirQualifierResolver
import org.jetbrains.kotlin.fir.scopes.FirImportingScope
import org.jetbrains.kotlin.name.Name
class FirExplicitImportingScope(imports: List<FirImport>) : FirImportingScope {
// TODO: Resolve imports! Instead of computing it resolution results here
private val simpleImports =
imports.filter { !it.isAllUnder && it.aliasName == null && it.importedFqName != null }
.mapNotNull { it.resolve() }
.groupBy { it.classFqName.shortName() }
private fun FirImport.resolve(): UnambiguousFqName? {
val session = session
val qualifierResolver = FirQualifierResolver.getInstance(session)
return qualifierResolver.resolveImport(importedFqName!!)
}
override fun processClassifiersByName(name: Name, processor: (UnambiguousFqName) -> Boolean): Boolean {
val imports = simpleImports[name] ?: return true
for (import in imports) {
if (!processor(import)) {
return false
}
}
return true
}
}
@@ -0,0 +1,32 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.fir.scopes.impl
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.UnambiguousFqName
import org.jetbrains.kotlin.fir.resolve.FirProvider
import org.jetbrains.kotlin.fir.resolve.FirQualifierResolver
import org.jetbrains.kotlin.fir.scopes.FirImportingScope
import org.jetbrains.kotlin.fir.service
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.name.Name
class FirSelfImportingScope(val fqName: FqNameUnsafe, val session: FirSession) : FirImportingScope {
override fun processClassifiersByName(name: Name, processor: (UnambiguousFqName) -> Boolean): Boolean {
val unambiguousFqName = UnambiguousFqName(fqName, FqName.topLevel(name))
val firProvider = session.service<FirProvider>()
if (firProvider.getFirClassifierByFqName(unambiguousFqName) != null) {
return processor(unambiguousFqName)
} else {
return true
}
}
}