blob: 5b8f53dd7ccc99adcc6e126577d71d2b282fcd27 [file] [log] [blame]
/*
* Copyright 2010-2020 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.analysis.low.level.api.fir
import junit.framework.TestCase
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDeclarationDesignation
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.RawFirNonLocalDeclarationBuilder
import org.jetbrains.kotlin.analysis.low.level.api.fir.test.base.AbstractLowLevelApiSingleFileTest
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.builder.PsiHandlingMode
import org.jetbrains.kotlin.fir.builder.RawFirBuilder
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.scopes.FirContainingNamesAwareScope
import org.jetbrains.kotlin.fir.scopes.FirScopeProvider
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
import org.jetbrains.kotlin.fir.session.FirSessionFactory
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.services.JUnit5Assertions
import org.jetbrains.kotlin.test.services.TestModuleStructure
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.assertions
import kotlin.io.path.readText
abstract class AbstractPartialRawFirBuilderTestCase : AbstractLowLevelApiSingleFileTest() {
override fun doTestByFileStructure(ktFile: KtFile, moduleStructure: TestModuleStructure, testServices: TestServices) {
val fileText = testDataPath.readText()
val functionName = InTextDirectivesUtils.findStringWithPrefixes(fileText, FUNCTION_DIRECTIVE)
val propertyName = InTextDirectivesUtils.findStringWithPrefixes(fileText, PROPERTY_DIRECTIVE)
when {
functionName != null -> testFunctionPartialBuilding(ktFile, functionName)
propertyName != null -> testPropertyPartialBuilding(ktFile, propertyName)
else -> testServices.assertions.fail { "No '$FUNCTION_DIRECTIVE' or '$PROPERTY_DIRECTIVE' directives found!" }
}
}
private fun testFunctionPartialBuilding(ktFile: KtFile, nameToFind: String) {
testPartialBuilding(
ktFile
) { file -> file.findDescendantOfType<KtNamedFunction> { it.name == nameToFind }!! }
}
private fun testPropertyPartialBuilding(ktFile: KtFile, nameToFind: String) {
testPartialBuilding(
ktFile
) { file -> file.findDescendantOfType<KtProperty> { it.name == nameToFind }!! }
}
private class DesignationBuilder(private val elementToBuild: KtDeclaration) : FirVisitorVoid() {
private val path = mutableListOf<FirDeclaration>()
var resultDesignation: FirDeclarationDesignation? = null
private set
override fun visitElement(element: FirElement) {
if (resultDesignation != null) return
when (element) {
is FirSimpleFunction, is FirProperty -> {
if (element.psi == elementToBuild) {
val originalDeclaration = element as FirDeclaration
resultDesignation = FirDeclarationDesignation(path, originalDeclaration)
} else {
element.acceptChildren(this)
}
}
is FirRegularClass -> {
path.add(element)
element.acceptChildren(this)
if (resultDesignation == null) {
path.removeLast()
}
}
else -> {
element.acceptChildren(this)
}
}
}
}
private fun <T : KtElement> testPartialBuilding(
file: KtFile,
findPsiElement: (KtFile) -> T
) {
val elementToBuild = findPsiElement(file) as KtDeclaration
val scopeProvider = object : FirScopeProvider() {
override fun getUseSiteMemberScope(klass: FirClass, useSiteSession: FirSession, scopeSession: ScopeSession): FirTypeScope =
error("Should not be called")
override fun getStaticMemberScopeForCallables(
klass: FirClass,
useSiteSession: FirSession,
scopeSession: ScopeSession
): FirContainingNamesAwareScope? =
error("Should not be called")
override fun getNestedClassifierScope(klass: FirClass, useSiteSession: FirSession, scopeSession: ScopeSession): FirContainingNamesAwareScope? =
error("Should not be called")
}
val session = FirSessionFactory.createEmptySession()
val firBuilder = RawFirBuilder(session, scopeProvider, PsiHandlingMode.IDE)
val original = firBuilder.buildFirFile(file)
val designationBuilder = DesignationBuilder(elementToBuild)
original.accept(designationBuilder)
val designation = designationBuilder.resultDesignation
TestCase.assertTrue(designation != null)
val firElement = RawFirNonLocalDeclarationBuilder.buildWithReplacement(
session = session,
scopeProvider = scopeProvider,
designation!!,
elementToBuild,
null
)
val firDump = firElement.render(FirRenderer.RenderMode.WithFqNames)
JUnit5Assertions.assertEqualsToTestDataFileSibling(firDump)
}
companion object {
private const val FUNCTION_DIRECTIVE = "// FUNCTION: "
private const val PROPERTY_DIRECTIVE = "// PROPERTY: "
}
}