Implement FirProvider consistency checker

This commit is contained in:
Simon Ogorodnik
2019-03-20 22:50:11 +03:00
committed by Mikhail Glukhikh
parent 1dae135840
commit 5f0fe4785d
2 changed files with 86 additions and 17 deletions
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.resolve.impl package org.jetbrains.kotlin.fir.resolve.impl
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
@@ -24,7 +25,7 @@ import org.jetbrains.kotlin.name.Name
class FirProviderImpl(val session: FirSession) : FirProvider { class FirProviderImpl(val session: FirSession) : FirProvider {
override fun getFirCallableContainerFile(symbol: ConeCallableSymbol): FirFile? { override fun getFirCallableContainerFile(symbol: ConeCallableSymbol): FirFile? {
return callableContainerMap[symbol] return state.callableContainerMap[symbol]
} }
override fun getClassLikeSymbolByFqName(classId: ClassId): ConeClassLikeSymbol? { override fun getClassLikeSymbolByFqName(classId: ClassId): ConeClassLikeSymbol? {
@@ -32,39 +33,39 @@ class FirProviderImpl(val session: FirSession) : FirProvider {
} }
override fun getTopLevelCallableSymbols(packageFqName: FqName, name: Name): List<ConeCallableSymbol> { override fun getTopLevelCallableSymbols(packageFqName: FqName, name: Name): List<ConeCallableSymbol> {
return (callableMap[CallableId(packageFqName, null, name)] ?: emptyList()) return (state.callableMap[CallableId(packageFqName, null, name)] ?: emptyList())
} }
override fun getClassDeclaredMemberScope(classId: ClassId) = override fun getClassDeclaredMemberScope(classId: ClassId) =
(getFirClassifierByFqName(classId) as? FirRegularClass)?.let(::FirClassDeclaredMemberScope) (getFirClassifierByFqName(classId) as? FirRegularClass)?.let(::FirClassDeclaredMemberScope)
override fun getFirClassifierContainerFile(fqName: ClassId): FirFile { override fun getFirClassifierContainerFile(fqName: ClassId): FirFile {
return classifierContainerFileMap[fqName] ?: error("Couldn't find container for $fqName") return state.classifierContainerFileMap[fqName] ?: error("Couldn't find container for $fqName")
} }
fun recordFile(file: FirFile) { fun recordFile(file: FirFile) {
recordFile(file, state)
}
private fun recordFile(file: FirFile, state: State) = with(state) {
val packageName = file.packageFqName val packageName = file.packageFqName
fileMap.merge(packageName, listOf(file)) { a, b -> a + b } fileMap.merge(packageName, listOf(file)) { a, b -> a + b }
file.acceptChildren(object : FirVisitorVoid() { file.acceptChildren(object : FirVisitorVoid() {
override fun visitElement(element: FirElement) {} override fun visitElement(element: FirElement) {}
var containerFqName: FqName = FqName.ROOT
override fun visitRegularClass(regularClass: FirRegularClass) { override fun visitRegularClass(regularClass: FirRegularClass) {
val fqName = containerFqName.child(regularClass.name) val classId = regularClass.symbol.classId
val classId = ClassId(packageName, fqName, false)
classifierMap[classId] = regularClass classifierMap[classId] = regularClass
classifierContainerFileMap[classId] = file classifierContainerFileMap[classId] = file
containerFqName = fqName
regularClass.acceptChildren(this) regularClass.acceptChildren(this)
containerFqName = fqName.parent()
} }
override fun visitTypeAlias(typeAlias: FirTypeAlias) { override fun visitTypeAlias(typeAlias: FirTypeAlias) {
val fqName = containerFqName.child(typeAlias.name) val classId = typeAlias.symbol.classId
val classId = ClassId(packageName, fqName, false)
classifierMap[classId] = typeAlias classifierMap[classId] = typeAlias
classifierContainerFileMap[classId] = file classifierContainerFileMap[classId] = file
} }
@@ -90,18 +91,75 @@ class FirProviderImpl(val session: FirSession) : FirProvider {
}) })
} }
private val fileMap = mutableMapOf<FqName, List<FirFile>>() private val state = State()
private val classifierMap = mutableMapOf<ClassId, FirClassLikeDeclaration>()
private val classifierContainerFileMap = mutableMapOf<ClassId, FirFile>() private class State {
private val callableMap = mutableMapOf<CallableId, List<ConeCallableSymbol>>() val fileMap = mutableMapOf<FqName, List<FirFile>>()
private val callableContainerMap = mutableMapOf<ConeCallableSymbol, FirFile>() val classifierMap = mutableMapOf<ClassId, FirClassLikeDeclaration>()
val classifierContainerFileMap = mutableMapOf<ClassId, FirFile>()
val callableMap = mutableMapOf<CallableId, List<ConeCallableSymbol>>()
val callableContainerMap = mutableMapOf<ConeCallableSymbol, FirFile>()
}
override fun getFirFilesByPackage(fqName: FqName): List<FirFile> { override fun getFirFilesByPackage(fqName: FqName): List<FirFile> {
return fileMap[fqName].orEmpty() return state.fileMap[fqName].orEmpty()
} }
override fun getFirClassifierByFqName(fqName: ClassId): FirClassLikeDeclaration? { override fun getFirClassifierByFqName(fqName: ClassId): FirClassLikeDeclaration? {
return classifierMap[fqName] return state.classifierMap[fqName]
}
@TestOnly
fun ensureConsistent(files: List<FirFile>) {
val newState = State()
files.forEach { recordFile(it, newState) }
fun <K, V> checkMapDiff(a: Map<K, V>, b: Map<K, V>, equal: (old: V?, new: V?) -> Boolean = { old, new -> old === new }) {
val unionKeys = a.keys + b.keys
val failures = mutableListOf<String>()
for ((key, aValue, bValue) in unionKeys.map { Triple(it, a[it], b[it]) }) {
if (!equal(aValue, bValue)) {
failures += "diff at key = '$key': was: '$aValue', become: '$bValue'"
}
}
assert(failures.isEmpty()) {
failures.joinToString(separator = "\n")
}
}
fun <K, V> checkMMapDiff(a: Map<K, List<V>>, b: Map<K, List<V>>) {
val unionKeys = a.keys + b.keys
val failures = mutableListOf<String>()
for ((key, aValue, bValue) in unionKeys.map { Triple(it, a[it], b[it])}) {
if (aValue == null || bValue == null) {
failures += "diff at key = '$key': was: $aValue, become: $bValue"
} else {
val aSet = aValue.toSet()
val bSet = bValue.toSet()
val aLost = aSet - bSet
val bNew = bSet - aSet
if (aLost.isNotEmpty() || bNew.isNotEmpty()) {
failures += "diff at key = '$key':"
failures += " Lost:"
aLost.forEach { failures += " $it" }
failures += " New:"
bNew.forEach { failures += " $it" }
}
}
}
assert(failures.isEmpty()) {
failures.joinToString(separator = "\n")
}
}
checkMMapDiff(state.fileMap, newState.fileMap)
checkMapDiff(state.classifierMap, newState.classifierMap)
checkMapDiff(state.classifierContainerFileMap, newState.classifierContainerFileMap)
checkMMapDiff(state.callableMap, newState.callableMap)
checkMapDiff(state.callableContainerMap, newState.callableContainerMap)
} }
override fun getClassUseSiteMemberScope(classId: ClassId, useSiteSession: FirSession): FirScope? { override fun getClassUseSiteMemberScope(classId: ClassId, useSiteSession: FirSession): FirScope? {
@@ -7,6 +7,8 @@ package org.jetbrains.kotlin.fir
import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.fir.declarations.FirFile import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.resolve.FirProvider
import org.jetbrains.kotlin.fir.resolve.impl.FirProviderImpl
import org.jetbrains.kotlin.fir.types.ConeClassErrorType import org.jetbrains.kotlin.fir.types.ConeClassErrorType
import org.jetbrains.kotlin.fir.types.ConeKotlinErrorType import org.jetbrains.kotlin.fir.types.ConeKotlinErrorType
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
@@ -19,6 +21,14 @@ import kotlin.math.max
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.system.measureNanoTime import kotlin.system.measureNanoTime
fun checkFirProvidersConsistency(firFiles: List<FirFile>) {
for ((session, files) in firFiles.groupBy { it.session }) {
val provider = session.service<FirProvider>() as FirProviderImpl
provider.ensureConsistent(files)
}
}
fun doFirResolveTestBench( fun doFirResolveTestBench(
firFiles: List<FirFile>, firFiles: List<FirFile>,
transformers: List<FirTransformer<Nothing?>>, transformers: List<FirTransformer<Nothing?>>,
@@ -62,6 +72,7 @@ fun doFirResolveTestBench(
} }
//totalLength += StringBuilder().apply { FirRenderer(this).visitFile(firFile) }.length //totalLength += StringBuilder().apply { FirRenderer(this).visitFile(firFile) }.length
} }
checkFirProvidersConsistency(firFiles)
} }
if (fails.none()) { if (fails.none()) {