From 5f0fe4785d8eac8dbdfdaa9e3def18e758f6568b Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Wed, 20 Mar 2019 22:50:11 +0300 Subject: [PATCH] Implement FirProvider consistency checker --- .../fir/resolve/impl/FirProviderImpl.kt | 92 +++++++++++++++---- .../jetbrains/kotlin/fir/FirResolveBench.kt | 11 +++ 2 files changed, 86 insertions(+), 17 deletions(-) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/impl/FirProviderImpl.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/impl/FirProviderImpl.kt index 680af878250..7d491561040 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/impl/FirProviderImpl.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/impl/FirProviderImpl.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.fir.resolve.impl +import org.jetbrains.annotations.TestOnly import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.* @@ -24,7 +25,7 @@ import org.jetbrains.kotlin.name.Name class FirProviderImpl(val session: FirSession) : FirProvider { override fun getFirCallableContainerFile(symbol: ConeCallableSymbol): FirFile? { - return callableContainerMap[symbol] + return state.callableContainerMap[symbol] } override fun getClassLikeSymbolByFqName(classId: ClassId): ConeClassLikeSymbol? { @@ -32,39 +33,39 @@ class FirProviderImpl(val session: FirSession) : FirProvider { } override fun getTopLevelCallableSymbols(packageFqName: FqName, name: Name): List { - return (callableMap[CallableId(packageFqName, null, name)] ?: emptyList()) + return (state.callableMap[CallableId(packageFqName, null, name)] ?: emptyList()) } override fun getClassDeclaredMemberScope(classId: ClassId) = (getFirClassifierByFqName(classId) as? FirRegularClass)?.let(::FirClassDeclaredMemberScope) 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) { + recordFile(file, state) + } + + private fun recordFile(file: FirFile, state: State) = with(state) { val packageName = file.packageFqName fileMap.merge(packageName, listOf(file)) { a, b -> a + b } file.acceptChildren(object : FirVisitorVoid() { override fun visitElement(element: FirElement) {} - var containerFqName: FqName = FqName.ROOT override fun visitRegularClass(regularClass: FirRegularClass) { - val fqName = containerFqName.child(regularClass.name) - val classId = ClassId(packageName, fqName, false) + val classId = regularClass.symbol.classId + classifierMap[classId] = regularClass classifierContainerFileMap[classId] = file - containerFqName = fqName regularClass.acceptChildren(this) - containerFqName = fqName.parent() } override fun visitTypeAlias(typeAlias: FirTypeAlias) { - val fqName = containerFqName.child(typeAlias.name) - val classId = ClassId(packageName, fqName, false) + val classId = typeAlias.symbol.classId classifierMap[classId] = typeAlias classifierContainerFileMap[classId] = file } @@ -90,18 +91,75 @@ class FirProviderImpl(val session: FirSession) : FirProvider { }) } - private val fileMap = mutableMapOf>() - private val classifierMap = mutableMapOf() - private val classifierContainerFileMap = mutableMapOf() - private val callableMap = mutableMapOf>() - private val callableContainerMap = mutableMapOf() + private val state = State() + + private class State { + val fileMap = mutableMapOf>() + val classifierMap = mutableMapOf() + val classifierContainerFileMap = mutableMapOf() + val callableMap = mutableMapOf>() + val callableContainerMap = mutableMapOf() + } override fun getFirFilesByPackage(fqName: FqName): List { - return fileMap[fqName].orEmpty() + return state.fileMap[fqName].orEmpty() } override fun getFirClassifierByFqName(fqName: ClassId): FirClassLikeDeclaration? { - return classifierMap[fqName] + return state.classifierMap[fqName] + } + + @TestOnly + fun ensureConsistent(files: List) { + val newState = State() + files.forEach { recordFile(it, newState) } + + fun checkMapDiff(a: Map, b: Map, equal: (old: V?, new: V?) -> Boolean = { old, new -> old === new }) { + val unionKeys = a.keys + b.keys + val failures = mutableListOf() + 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 checkMMapDiff(a: Map>, b: Map>) { + val unionKeys = a.keys + b.keys + val failures = mutableListOf() + 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? { diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/fir/FirResolveBench.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/fir/FirResolveBench.kt index 7a0d4c05f1e..f849de96a7b 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/fir/FirResolveBench.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/fir/FirResolveBench.kt @@ -7,6 +7,8 @@ package org.jetbrains.kotlin.fir import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.psi.PsiElement 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.ConeKotlinErrorType import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef @@ -19,6 +21,14 @@ import kotlin.math.max import kotlin.reflect.KClass import kotlin.system.measureNanoTime + +fun checkFirProvidersConsistency(firFiles: List) { + for ((session, files) in firFiles.groupBy { it.session }) { + val provider = session.service() as FirProviderImpl + provider.ensureConsistent(files) + } +} + fun doFirResolveTestBench( firFiles: List, transformers: List>, @@ -62,6 +72,7 @@ fun doFirResolveTestBench( } //totalLength += StringBuilder().apply { FirRenderer(this).visitFile(firFile) }.length } + checkFirProvidersConsistency(firFiles) } if (fails.none()) {