FIR IDE: add test which check that every declaration is visited single time during diagnostic collection
This commit is contained in:
+4
-2
@@ -68,13 +68,15 @@ abstract class AbstractDiagnosticCollector(
|
|||||||
componentsInitialized = true
|
componentsInitialized = true
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun beforeCollecting() {}
|
protected open fun beforeRunningAllComponentsOnElement(element: FirElement) {}
|
||||||
|
protected open fun beforeRunningSingleComponentOnElement(element: FirElement) {}
|
||||||
|
|
||||||
private inner class Visitor : FirDefaultVisitor<Unit, Nothing?>() {
|
private inner class Visitor : FirDefaultVisitor<Unit, Nothing?>() {
|
||||||
private fun <T : FirElement> T.runComponents() {
|
private fun <T : FirElement> T.runComponents() {
|
||||||
if (currentAction.checkInCurrentDeclaration) {
|
if (currentAction.checkInCurrentDeclaration) {
|
||||||
|
beforeRunningAllComponentsOnElement(this)
|
||||||
components.forEach {
|
components.forEach {
|
||||||
beforeCollecting()
|
beforeRunningSingleComponentOnElement(this)
|
||||||
this.accept(it, context)
|
this.accept(it, context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ import org.jetbrains.kotlin.idea.fir.low.level.api.AbstractFirLazyDeclarationRes
|
|||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.AbstractFirLazyResolveTest
|
import org.jetbrains.kotlin.idea.fir.low.level.api.AbstractFirLazyResolveTest
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.AbstractFirMultiModuleLazyResolveTest
|
import org.jetbrains.kotlin.idea.fir.low.level.api.AbstractFirMultiModuleLazyResolveTest
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.AbstractFirMultiModuleResolveTest
|
import org.jetbrains.kotlin.idea.fir.low.level.api.AbstractFirMultiModuleResolveTest
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.diagnostic.AbstractDiagnosticTraversalCounterTest
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.file.structure.AbstractFileStructureAndOutOfBlockModificationTrackerConsistencyTest
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.structure.AbstractFileStructureAndOutOfBlockModificationTrackerConsistencyTest
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.file.structure.AbstractFileStructureTest
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.structure.AbstractFileStructureTest
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.AbstractSessionsInvalidationTest
|
import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.AbstractSessionsInvalidationTest
|
||||||
@@ -1073,6 +1074,9 @@ fun main(args: Array<String>) {
|
|||||||
testClass<AbstractFileStructureTest> {
|
testClass<AbstractFileStructureTest> {
|
||||||
model("fileStructure")
|
model("fileStructure")
|
||||||
}
|
}
|
||||||
|
testClass<AbstractDiagnosticTraversalCounterTest> {
|
||||||
|
model("diagnosticTraversalCounter")
|
||||||
|
}
|
||||||
testClass<AbstractSessionsInvalidationTest> {
|
testClass<AbstractSessionsInvalidationTest> {
|
||||||
model("sessionInvalidation", recursive = false, extension = null)
|
model("sessionInvalidation", recursive = false, extension = null)
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-4
@@ -13,8 +13,8 @@ import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
|||||||
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
|
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState
|
import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.FirLazyDeclarationResolver
|
import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.FirLazyDeclarationResolver
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.FirIdeSession
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.FirIdeSessionProviderStorage
|
import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.FirIdeSessionProviderStorage
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.trackers.KotlinFirOutOfBlockModificationTrackerFactory
|
|
||||||
import org.jetbrains.kotlin.idea.util.cachedValue
|
import org.jetbrains.kotlin.idea.util.cachedValue
|
||||||
import org.jetbrains.kotlin.idea.util.getValue
|
import org.jetbrains.kotlin.idea.util.getValue
|
||||||
import org.jetbrains.kotlin.trackers.createProjectWideOutOfBlockModificationTracker
|
import org.jetbrains.kotlin.trackers.createProjectWideOutOfBlockModificationTracker
|
||||||
@@ -39,12 +39,13 @@ internal class FirIdeResolveStateService(project: Project) {
|
|||||||
|
|
||||||
internal fun createResolveStateFor(
|
internal fun createResolveStateFor(
|
||||||
moduleInfo: IdeaModuleInfo,
|
moduleInfo: IdeaModuleInfo,
|
||||||
sessionProviderStorage: FirIdeSessionProviderStorage
|
sessionProviderStorage: FirIdeSessionProviderStorage,
|
||||||
|
configureSession: (FirIdeSession.() -> Unit)? = null,
|
||||||
): FirModuleResolveStateImpl {
|
): FirModuleResolveStateImpl {
|
||||||
if (moduleInfo !is ModuleSourceInfo) {
|
if (moduleInfo !is ModuleSourceInfo) {
|
||||||
error("Creating FirModuleResolveState is not yet supported for $moduleInfo")
|
error("Creating FirModuleResolveState is not yet supported for $moduleInfo")
|
||||||
}
|
}
|
||||||
val sessionProvider = sessionProviderStorage.getSessionProvider(moduleInfo)
|
val sessionProvider = sessionProviderStorage.getSessionProvider(moduleInfo, configureSession)
|
||||||
val firFileBuilder = sessionProvider.rootModuleSession.firFileBuilder
|
val firFileBuilder = sessionProvider.rootModuleSession.firFileBuilder
|
||||||
return FirModuleResolveStateImpl(
|
return FirModuleResolveStateImpl(
|
||||||
moduleInfo.project,
|
moduleInfo.project,
|
||||||
@@ -60,6 +61,12 @@ internal class FirIdeResolveStateService(project: Project) {
|
|||||||
@TestOnly
|
@TestOnly
|
||||||
fun createResolveStateForNoCaching(
|
fun createResolveStateForNoCaching(
|
||||||
moduleInfo: IdeaModuleInfo,
|
moduleInfo: IdeaModuleInfo,
|
||||||
): FirModuleResolveState = FirIdeResolveStateService.createResolveStateFor(moduleInfo, FirIdeSessionProviderStorage(moduleInfo.project!!))
|
configureSession: (FirIdeSession.() -> Unit)? = null,
|
||||||
|
): FirModuleResolveState =
|
||||||
|
FirIdeResolveStateService.createResolveStateFor(
|
||||||
|
moduleInfo = moduleInfo,
|
||||||
|
sessionProviderStorage = FirIdeSessionProviderStorage(moduleInfo.project!!),
|
||||||
|
configureSession = configureSession
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+9
-1
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.fir.low.level.api.diagnostics
|
package org.jetbrains.kotlin.idea.fir.low.level.api.diagnostics
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.fir.FirElement
|
||||||
import org.jetbrains.kotlin.fir.FirSession
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
import org.jetbrains.kotlin.fir.SessionConfiguration
|
import org.jetbrains.kotlin.fir.SessionConfiguration
|
||||||
import org.jetbrains.kotlin.fir.analysis.CheckersComponent
|
import org.jetbrains.kotlin.fir.analysis.CheckersComponent
|
||||||
@@ -41,6 +42,9 @@ internal abstract class AbstractFirIdeDiagnosticsCollector(
|
|||||||
::FirIdeDesignatedBodyResolveTransformerForReturnTypeCalculator
|
::FirIdeDesignatedBodyResolveTransformerForReturnTypeCalculator
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
|
private val beforeElementDiagnosticCollectionHandler: BeforeElementDiagnosticCollectionHandler? =
|
||||||
|
session.beforeElementDiagnosticCollectionHandler
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val declarationCheckers = CheckersFactory.createDeclarationCheckers(useExtendedCheckers)
|
val declarationCheckers = CheckersFactory.createDeclarationCheckers(useExtendedCheckers)
|
||||||
val expressionCheckers = CheckersFactory.createExpressionCheckers(useExtendedCheckers)
|
val expressionCheckers = CheckersFactory.createExpressionCheckers(useExtendedCheckers)
|
||||||
@@ -70,10 +74,14 @@ internal abstract class AbstractFirIdeDiagnosticsCollector(
|
|||||||
reporter = Reporter()
|
reporter = Reporter()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun beforeCollecting() {
|
override fun beforeRunningSingleComponentOnElement(element: FirElement) {
|
||||||
checkCanceled()
|
checkCanceled()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun beforeRunningAllComponentsOnElement(element: FirElement) {
|
||||||
|
beforeElementDiagnosticCollectionHandler?.beforeCollectingForElement(element)
|
||||||
|
}
|
||||||
|
|
||||||
override fun getCollectedDiagnostics(): List<FirDiagnostic<*>> {
|
override fun getCollectedDiagnostics(): List<FirDiagnostic<*>> {
|
||||||
// Not necessary in IDE
|
// Not necessary in IDE
|
||||||
return emptyList()
|
return emptyList()
|
||||||
|
|||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* 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.idea.fir.low.level.api.diagnostics
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.fir.FirElement
|
||||||
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
|
import org.jetbrains.kotlin.fir.FirSessionComponent
|
||||||
|
|
||||||
|
abstract class BeforeElementDiagnosticCollectionHandler: FirSessionComponent {
|
||||||
|
abstract fun beforeCollectingForElement(element: FirElement)
|
||||||
|
}
|
||||||
|
|
||||||
|
val FirSession.beforeElementDiagnosticCollectionHandler: BeforeElementDiagnosticCollectionHandler? by FirSession.nullableSessionComponentAccessor()
|
||||||
+11
-2
@@ -58,6 +58,7 @@ internal object FirIdeSessionFactory {
|
|||||||
isRootModule: Boolean,
|
isRootModule: Boolean,
|
||||||
librariesCache: LibrariesCache,
|
librariesCache: LibrariesCache,
|
||||||
languageVersionSettings: LanguageVersionSettings = LanguageVersionSettingsImpl.DEFAULT,
|
languageVersionSettings: LanguageVersionSettings = LanguageVersionSettingsImpl.DEFAULT,
|
||||||
|
configureSession: (FirIdeSession.() -> Unit)? = null
|
||||||
): FirIdeSourcesSession {
|
): FirIdeSourcesSession {
|
||||||
sessionsCache[moduleInfo]?.let { return it }
|
sessionsCache[moduleInfo]?.let { return it }
|
||||||
val scopeProvider = KotlinScopeProvider(::wrapScopeWithJvmMapped)
|
val scopeProvider = KotlinScopeProvider(::wrapScopeWithJvmMapped)
|
||||||
@@ -108,7 +109,8 @@ internal object FirIdeSessionFactory {
|
|||||||
project,
|
project,
|
||||||
builtinsAndCloneableSession,
|
builtinsAndCloneableSession,
|
||||||
builtinTypes,
|
builtinTypes,
|
||||||
librariesCache
|
librariesCache,
|
||||||
|
configureSession = configureSession,
|
||||||
).symbolProvider
|
).symbolProvider
|
||||||
)
|
)
|
||||||
dependentModules
|
dependentModules
|
||||||
@@ -123,6 +125,7 @@ internal object FirIdeSessionFactory {
|
|||||||
sessionsCache,
|
sessionsCache,
|
||||||
isRootModule = false,
|
isRootModule = false,
|
||||||
librariesCache,
|
librariesCache,
|
||||||
|
configureSession = configureSession,
|
||||||
).symbolProvider
|
).symbolProvider
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,6 +140,8 @@ internal object FirIdeSessionFactory {
|
|||||||
registerExtendedCommonCheckers()
|
registerExtendedCommonCheckers()
|
||||||
}
|
}
|
||||||
}.configure()
|
}.configure()
|
||||||
|
|
||||||
|
configureSession?.invoke(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,6 +152,7 @@ internal object FirIdeSessionFactory {
|
|||||||
builtinTypes: BuiltinTypes,
|
builtinTypes: BuiltinTypes,
|
||||||
librariesCache: LibrariesCache,
|
librariesCache: LibrariesCache,
|
||||||
languageVersionSettings: LanguageVersionSettings = LanguageVersionSettingsImpl.DEFAULT,
|
languageVersionSettings: LanguageVersionSettings = LanguageVersionSettingsImpl.DEFAULT,
|
||||||
|
configureSession: (FirIdeSession.() -> Unit)?,
|
||||||
): FirIdeLibrariesSession = librariesCache.cached(moduleInfo) {
|
): FirIdeLibrariesSession = librariesCache.cached(moduleInfo) {
|
||||||
checkCanceled()
|
checkCanceled()
|
||||||
val searchScope = ModuleLibrariesSearchScope(moduleInfo.module)
|
val searchScope = ModuleLibrariesSearchScope(moduleInfo.module)
|
||||||
@@ -191,13 +197,15 @@ internal object FirIdeSessionFactory {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
register(FirJvmTypeMapper::class, FirJvmTypeMapper(this))
|
register(FirJvmTypeMapper::class, FirJvmTypeMapper(this))
|
||||||
|
configureSession?.invoke(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createBuiltinsAndCloneableSession(
|
fun createBuiltinsAndCloneableSession(
|
||||||
project: Project,
|
project: Project,
|
||||||
builtinTypes: BuiltinTypes,
|
builtinTypes: BuiltinTypes,
|
||||||
languageVersionSettings: LanguageVersionSettings = LanguageVersionSettingsImpl.DEFAULT
|
languageVersionSettings: LanguageVersionSettings = LanguageVersionSettingsImpl.DEFAULT,
|
||||||
|
configureSession: (FirIdeSession.() -> Unit)? = null,
|
||||||
): FirIdeBuiltinsAndCloneableSession {
|
): FirIdeBuiltinsAndCloneableSession {
|
||||||
return FirIdeBuiltinsAndCloneableSession(project, builtinTypes).apply {
|
return FirIdeBuiltinsAndCloneableSession(project, builtinTypes).apply {
|
||||||
registerIdeComponents()
|
registerIdeComponents()
|
||||||
@@ -215,6 +223,7 @@ internal object FirIdeSessionFactory {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
register(FirJvmTypeMapper::class, FirJvmTypeMapper(this))
|
register(FirJvmTypeMapper::class, FirJvmTypeMapper(this))
|
||||||
|
configureSession?.invoke(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-1
@@ -26,7 +26,10 @@ internal class FirIdeSessionProviderStorage(private val project: Project) {
|
|||||||
|
|
||||||
private val librariesCache by cachedValue(project, LibraryModificationTracker.getInstance(project)) { LibrariesCache() }
|
private val librariesCache by cachedValue(project, LibraryModificationTracker.getInstance(project)) { LibrariesCache() }
|
||||||
|
|
||||||
fun getSessionProvider(rootModule: ModuleSourceInfo): FirIdeSessionProvider {
|
fun getSessionProvider(
|
||||||
|
rootModule: ModuleSourceInfo,
|
||||||
|
configureSession: (FirIdeSession.() -> Unit)? = null
|
||||||
|
): FirIdeSessionProvider {
|
||||||
val firPhaseRunner = FirPhaseRunner()
|
val firPhaseRunner = FirPhaseRunner()
|
||||||
|
|
||||||
val builtinTypes = BuiltinTypes()
|
val builtinTypes = BuiltinTypes()
|
||||||
@@ -45,6 +48,7 @@ internal class FirIdeSessionProviderStorage(private val project: Project) {
|
|||||||
sessions,
|
sessions,
|
||||||
isRootModule = true,
|
isRootModule = true,
|
||||||
librariesCache,
|
librariesCache,
|
||||||
|
configureSession = configureSession,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
sessions to session
|
sessions to session
|
||||||
|
|||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun foo(f: Float.(Int, String) -> Boolean) {
|
||||||
|
|
||||||
|
}
|
||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
class A(val x: Int = 10, val b: String) {
|
||||||
|
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun foo() {
|
||||||
|
fun local() = 0
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
fun foo() {
|
||||||
|
fun local() {
|
||||||
|
println("local")
|
||||||
|
}
|
||||||
|
}
|
||||||
idea/idea-frontend-fir/idea-fir-low-level-api/testdata/diagnosticTraversalCounter/memberFunctions.kt
Vendored
+7
@@ -0,0 +1,7 @@
|
|||||||
|
class A {
|
||||||
|
fun foo1() = 10
|
||||||
|
|
||||||
|
fun foo2() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
class A {
|
||||||
|
fun foo1() = 10
|
||||||
|
|
||||||
|
fun foo2() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
class A {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class B {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class C {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class D {
|
||||||
|
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
fun foo1() = 1
|
||||||
|
|
||||||
|
fun foo2() = 2
|
||||||
|
|
||||||
|
fun foo3() = 3
|
||||||
|
|
||||||
|
fun foo4() = 4
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
fun foo1() {
|
||||||
|
println("foo1")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo2() {
|
||||||
|
println("foo2")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo3() {
|
||||||
|
println("foo3")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo4() {
|
||||||
|
println("foo4")
|
||||||
|
}
|
||||||
Vendored
+11
@@ -0,0 +1,11 @@
|
|||||||
|
class A {
|
||||||
|
class B {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object C {
|
||||||
|
class D {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
var withGetterAndSetter: Int = 42
|
||||||
|
get() = field
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
class A(val x: Int = 10, val b: String) {
|
||||||
|
constructor(i: Int) : this(x = 1, b = i.toString())
|
||||||
|
}
|
||||||
+112
@@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* 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.idea.fir.low.level.api.diagnostic
|
||||||
|
|
||||||
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
|
import org.jetbrains.kotlin.fir.FirElement
|
||||||
|
import org.jetbrains.kotlin.fir.SessionConfiguration
|
||||||
|
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
|
||||||
|
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.api.DiagnosticCheckerFilter
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.api.collectDiagnosticsForFile
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.api.getOrBuildFir
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.createResolveStateForNoCaching
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.diagnostics.BeforeElementDiagnosticCollectionHandler
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.renderWithClassName
|
||||||
|
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that every declaration is visited exactly one time during diagnostic collection
|
||||||
|
*/
|
||||||
|
abstract class AbstractDiagnosticTraversalCounterTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||||
|
override fun isFirPlugin(): Boolean = true
|
||||||
|
|
||||||
|
fun doTest(path: String) {
|
||||||
|
val testDataFile = File(path)
|
||||||
|
val ktFile = myFixture.configureByText(testDataFile.name, FileUtil.loadFile(testDataFile)) as KtFile
|
||||||
|
|
||||||
|
val handler = BeforeElementTestDiagnosticCollectionHandler()
|
||||||
|
|
||||||
|
@OptIn(SessionConfiguration::class)
|
||||||
|
val resolveState = createResolveStateForNoCaching(ktFile.getModuleInfo()) {
|
||||||
|
register(BeforeElementDiagnosticCollectionHandler::class, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// we should get diagnostics before we resolve the whole file by ktFile.getOrBuildFir
|
||||||
|
ktFile.collectDiagnosticsForFile(resolveState, DiagnosticCheckerFilter.ONLY_COMMON_CHECKERS)
|
||||||
|
|
||||||
|
val firFile = ktFile.getOrBuildFir(resolveState)
|
||||||
|
|
||||||
|
val errorElements = collectErrorElements(firFile, handler)
|
||||||
|
|
||||||
|
if (errorElements.isNotEmpty()) {
|
||||||
|
val zeroElements = errorElements.filter { it.second == 0 }
|
||||||
|
val nonZeroElements = errorElements.filter { it.second > 1 }
|
||||||
|
val message = buildString {
|
||||||
|
if (zeroElements.isNotEmpty()) {
|
||||||
|
appendLine(
|
||||||
|
""" |The following elements were not visited
|
||||||
|
|${zeroElements.joinToString(separator = "\n\n") { it.first.renderWithClassName() }}
|
||||||
|
""".trimMargin()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (nonZeroElements.isNotEmpty()) {
|
||||||
|
appendLine(
|
||||||
|
""" |The following elements were visited more than one time
|
||||||
|
|${nonZeroElements.joinToString(separator = "\n\n") { it.second.toString() + " times " + it.first.renderWithClassName() }}
|
||||||
|
""".trimMargin()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fail(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun collectErrorElements(
|
||||||
|
firFile: FirElement,
|
||||||
|
handler: BeforeElementTestDiagnosticCollectionHandler
|
||||||
|
): List<Pair<FirElement, Int>> {
|
||||||
|
val errorElements = mutableListOf<Pair<FirElement, Int>>()
|
||||||
|
val nonDuplicatingElements = findNonDuplicatingFirElements(firFile)
|
||||||
|
firFile.accept(object : FirVisitorVoid() {
|
||||||
|
override fun visitElement(element: FirElement) {
|
||||||
|
if (element !in nonDuplicatingElements) return
|
||||||
|
val visitedTimes = handler.visitedTimes[element] ?: 0
|
||||||
|
if (visitedTimes != 1) {
|
||||||
|
errorElements += element to visitedTimes
|
||||||
|
}
|
||||||
|
element.acceptChildren(this)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return errorElements
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findNonDuplicatingFirElements(
|
||||||
|
firFile: FirElement,
|
||||||
|
): Set<FirElement> {
|
||||||
|
val elementUsageCount = mutableMapOf<FirElement, Int>()
|
||||||
|
firFile.accept(object : FirVisitorVoid() {
|
||||||
|
override fun visitElement(element: FirElement) {
|
||||||
|
elementUsageCount.compute(element) { _, count -> (count ?: 0) + 1 }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return elementUsageCount.filterValues { it == 1 }.keys
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class BeforeElementTestDiagnosticCollectionHandler : BeforeElementDiagnosticCollectionHandler() {
|
||||||
|
val visitedTimes = mutableMapOf<FirElement, Int>()
|
||||||
|
override fun beforeCollectingForElement(element: FirElement) {
|
||||||
|
if (!visitedTimes.containsKey(element)) {
|
||||||
|
visitedTimes[element] = 1
|
||||||
|
} else {
|
||||||
|
visitedTimes.compute(element) { _, count -> (count ?: 0) + 1 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+91
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* 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.idea.fir.low.level.api.diagnostic;
|
||||||
|
|
||||||
|
import com.intellij.testFramework.TestDataPath;
|
||||||
|
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||||
|
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||||
|
import org.jetbrains.kotlin.test.util.KtTestUtil;
|
||||||
|
import org.jetbrains.kotlin.test.TestMetadata;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
@TestMetadata("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/diagnosticTraversalCounter")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public class DiagnosticTraversalCounterTestGenerated extends AbstractDiagnosticTraversalCounterTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInDiagnosticTraversalCounter() throws Exception {
|
||||||
|
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/diagnosticTraversalCounter"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("constructor.kt")
|
||||||
|
public void testConstructor() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/diagnosticTraversalCounter/constructor.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionalType.kt")
|
||||||
|
public void testFunctionalType() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/diagnosticTraversalCounter/functionalType.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("localFunctionWithImplicitType.kt")
|
||||||
|
public void testLocalFunctionWithImplicitType() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/diagnosticTraversalCounter/localFunctionWithImplicitType.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("localUnitFunction.kt")
|
||||||
|
public void testLocalUnitFunction() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/diagnosticTraversalCounter/localUnitFunction.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("memberFunctions.kt")
|
||||||
|
public void testMemberFunctions() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/diagnosticTraversalCounter/memberFunctions.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("memberProperties.kt")
|
||||||
|
public void testMemberProperties() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/diagnosticTraversalCounter/memberProperties.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("multipleTopLevelClasses.kt")
|
||||||
|
public void testMultipleTopLevelClasses() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/diagnosticTraversalCounter/multipleTopLevelClasses.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("multipleTopLevelFunctionsWithImplicitTypes.kt")
|
||||||
|
public void testMultipleTopLevelFunctionsWithImplicitTypes() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/diagnosticTraversalCounter/multipleTopLevelFunctionsWithImplicitTypes.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("multipleTopLevelUnitFunctions.kt")
|
||||||
|
public void testMultipleTopLevelUnitFunctions() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/diagnosticTraversalCounter/multipleTopLevelUnitFunctions.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nestedClases.kt")
|
||||||
|
public void testNestedClases() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/diagnosticTraversalCounter/nestedClases.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("propertyWithGetterAndSetter.kt")
|
||||||
|
public void testPropertyWithGetterAndSetter() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/diagnosticTraversalCounter/propertyWithGetterAndSetter.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("secondaryConstructor.kt")
|
||||||
|
public void testSecondaryConstructor() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/diagnosticTraversalCounter/secondaryConstructor.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
@@ -9,6 +9,9 @@ import com.intellij.openapi.components.service
|
|||||||
import com.intellij.openapi.module.Module
|
import com.intellij.openapi.module.Module
|
||||||
import com.intellij.openapi.module.ModuleManager
|
import com.intellij.openapi.module.ModuleManager
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
|
import org.jetbrains.kotlin.fir.FirElement
|
||||||
|
import org.jetbrains.kotlin.fir.FirRenderer
|
||||||
|
import org.jetbrains.kotlin.fir.render
|
||||||
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
|
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState
|
import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.trackers.KotlinFirModificationTrackerService
|
import org.jetbrains.kotlin.idea.fir.low.level.api.trackers.KotlinFirModificationTrackerService
|
||||||
@@ -21,6 +24,9 @@ inline fun resolveWithClearCaches(context: KtElement, action: (FirModuleResolveS
|
|||||||
action(resolveState)
|
action(resolveState)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun FirElement.renderWithClassName(renderMode: FirRenderer.RenderMode = FirRenderer.RenderMode.Normal): String =
|
||||||
|
"${this::class.simpleName} `${render(renderMode)}`"
|
||||||
|
|
||||||
internal fun Module.incModificationTracker() {
|
internal fun Module.incModificationTracker() {
|
||||||
project.service<KotlinFirModificationTrackerService>().increaseModificationCountForModule(this)
|
project.service<KotlinFirModificationTrackerService>().increaseModificationCountForModule(this)
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user