Write some simple FIR dummy comparator

This commit is contained in:
Ivan Cilcic
2019-07-11 01:00:56 +03:00
committed by Mikhail Glukhikh
parent 5a24c2253a
commit bfe83d0bfd
6 changed files with 508 additions and 28 deletions

View File

@@ -880,7 +880,8 @@ class DeclarationsConverter(
*/
private fun visitBlock(block: LighterASTNode?): FirBlock {
return if (!stubMode) {
TODO("not implemented")
//TODO("not implemented")
FirBlockImpl(session, null)
//visitStatements(ctx.statements())
} else {
FirSingleExpressionBlock(

View File

@@ -34,7 +34,7 @@ class ExpressionsConverter(
STRING_TEMPLATE -> return convertStringTemplate(expression)
is KtConstantExpressionElementType -> return convertConstantExpression(expression)
}
TODO("not implemented")
//TODO("not implemented")
}
return FirExpressionStub(session, null)

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2010-2019 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.fir.lightTree.compare
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSessionBase
import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
import org.jetbrains.kotlin.fir.declarations.impl.FirVariableImpl
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.impl.FirUnknownTypeCallWithArgumentList
import org.jetbrains.kotlin.fir.expressions.impl.FirUnknownTypeExpression
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.fir.transformSingle
import org.jetbrains.kotlin.fir.types.FirImplicitTypeRef
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.types.impl.FirImplicitTypeRefImpl
import org.jetbrains.kotlin.fir.visitors.FirTransformer
import org.jetbrains.kotlin.fir.visitors.FirVisitor
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames
interface DummyElement
class DummyFirStatement(
psi: PsiElement? = null,
session: FirSession = object : FirSessionBase(null) {}
) : FirUnknownTypeExpression(session, psi), DummyElement
class DummyFirVariable(
override val psi: PsiElement? = null,
override val session: FirSession = object : FirSessionBase(null) {},
override val annotations: List<FirAnnotationCall> = mutableListOf(),
override val receiverTypeRef: FirTypeRef? = null,
override val name: Name = SpecialNames.NO_NAME_PROVIDED,
override var returnTypeRef: FirTypeRef = FirImplicitTypeRefImpl(session, psi),
override val isVar: Boolean = false,
override var initializer: FirExpression? = null,
override val symbol: FirVariableSymbol = FirVariableSymbol(name),
override var delegate: FirExpression? = null
) : FirVariable, DummyElement {
override fun <D> transformReturnTypeRef(transformer: FirTransformer<D>, data: D) {
returnTypeRef = returnTypeRef.transformSingle(transformer, data)
}
}
class DummyFirAnonymousFunction(
psi: PsiElement? = null,
session: FirSession = object : FirSessionBase(null) {},
override val valueParameters: List<FirValueParameter> = listOf(),
override val body: FirBlock? = null,
override val receiverTypeRef: FirTypeRef? = null,
override var returnTypeRef: FirTypeRef = FirImplicitTypeRefImpl(session, psi)
) : FirAnonymousFunction(session, psi),
DummyElement {
override fun <D> transformReturnTypeRef(transformer: FirTransformer<D>, data: D) {
returnTypeRef = returnTypeRef.transformSingle(transformer, data)
}
}
class DummyFirDeclaration(
override val psi: PsiElement? = null,
override val session: FirSession = object : FirSessionBase(null) {},
override val annotations: List<FirAnnotationCall> = listOf()
) : FirDeclaration, FirStatement, DummyElement {
override fun <R, D> accept(visitor: FirVisitor<R, D>, data: D): R =
visitor.visitDeclaration(this, data)
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2010-2019 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.fir.lightTree.compare
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
class FirVisitorForComparator : FirVisitorVoid() {
val firTreeNodesClasses: MutableList<String> = mutableListOf()
override fun visitElement(element: FirElement) {
if (element !is DummyElement) {
firTreeNodesClasses += element.javaClass.canonicalName
element.acceptChildren(this)
}
}
}
fun FirFile.areEqualTo(firFile: FirFile): Boolean {
val thisFirList = FirVisitorForComparator().apply { visitFile(this@areEqualTo) }.firTreeNodesClasses
val otherFirList = FirVisitorForComparator().apply { visitFile(firFile) }.firTreeNodesClasses
if (thisFirList.size != otherFirList.size) return false
for (i in 0 until thisFirList.size) {
if (thisFirList[i] != otherFirList[i]) {
return false
}
}
return true
}

View File

@@ -5,14 +5,93 @@
package org.jetbrains.kotlin.fir.lightTree.compare
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.impl.FirAbstractCallableMember
import org.jetbrains.kotlin.fir.declarations.impl.FirMemberFunctionImpl
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.impl.*
import org.jetbrains.kotlin.fir.resolve.transformers.FirAbstractTreeTransformer
import org.jetbrains.kotlin.fir.visitors.CompositeTransformResult
import org.jetbrains.kotlin.fir.visitors.compose
import org.jetbrains.kotlin.psi.KtForExpression
class FirPartialTransformer(
private val visitAnnotation: Boolean = true
private val visitAnonymousFunction: Boolean = false,
private val visitNamedFunction: Boolean = false,
private val visitMemberDeclaration: Boolean = false,
private val visitVariable: Boolean = false,
private val visitAnnotation: Boolean = false,
private val visitOperationCall: Boolean = false,
private val visitTypeOperatorCall: Boolean = false,
private val visitStringConcatenationCall: Boolean = false,
private val visitArrayOfCall: Boolean = false,
private val visitFunctionCall: Boolean = false,
private val visitGetClassCall: Boolean = false,
private val visitBreakExpression: Boolean = false,
private val visitContinueExpression: Boolean = false,
private val visitReturnExpression: Boolean = false,
private val visitThrowExpression: Boolean = false,
private val visitForLoop: Boolean = false,
private val visitClassReferenceExpression: Boolean = false,
private val visitConstExpression: Boolean = false,
private val visitQualifiedAccessExpression: Boolean = false,
private val visitCallableReferenceAccess: Boolean = false,
private val visitTryExpression: Boolean = false,
private val visitWhenExpression: Boolean = false,
private val visitNamedArgumentExpression: Boolean = false,
private val visitSpreadArgumentExpression: Boolean = false,
private val visitAnonymousObject: Boolean = false,
private val visitDoWhileLoop: Boolean = false,
private val visitWhileLoop: Boolean = false,
private val visitAssignment: Boolean = false
) : FirAbstractTreeTransformer() {
override fun transformAnonymousFunction(
anonymousFunction: FirAnonymousFunction,
data: Nothing?
): CompositeTransformResult<FirDeclaration> {
return if (visitAnonymousFunction) {
(anonymousFunction.transformChildren(this, data) as FirAnonymousFunction).compose()
} else {
DummyFirAnonymousFunction().compose()
}
}
override fun transformNamedFunction(namedFunction: FirNamedFunction, data: Nothing?): CompositeTransformResult<FirDeclaration> {
return if (!visitNamedFunction || (namedFunction is FirAbstractCallableMember && namedFunction.visibility == Visibilities.LOCAL)) {
DummyFirDeclaration().compose()
} else {
(namedFunction.transformChildren(this, data) as FirNamedFunction).compose()
}
}
override fun transformMemberDeclaration(
memberDeclaration: FirMemberDeclaration,
data: Nothing?
): CompositeTransformResult<FirDeclaration> {
return if (!visitMemberDeclaration || memberDeclaration.visibility == Visibilities.LOCAL) {
DummyFirDeclaration().compose()
} else {
(memberDeclaration.transformChildren(this, data) as FirMemberDeclaration).compose()
}
}
override fun transformVariable(variable: FirVariable, data: Nothing?): CompositeTransformResult<FirDeclaration> {
return if (visitVariable) {
(variable.transformChildren(this, data) as FirVariable).compose()
} else {
DummyFirVariable().compose()
}
}
override fun transformExpression(expression: FirExpression, data: Nothing?): CompositeTransformResult<FirStatement> {
return if (expression is FirExpressionStub) {
DummyFirStatement().compose()
} else {
super.transformExpression(expression, data)
}
}
override fun transformAnnotationCall(annotationCall: FirAnnotationCall, data: Nothing?): CompositeTransformResult<FirAnnotationCall> {
return if (visitAnnotation) {
(annotationCall.transformChildren(this, data) as FirAnnotationCall).compose()
@@ -20,4 +99,212 @@ class FirPartialTransformer(
CompositeTransformResult.empty()
}
}
override fun transformOperatorCall(operatorCall: FirOperatorCall, data: Nothing?): CompositeTransformResult<FirStatement> {
return if (visitOperationCall) {
(operatorCall.transformChildren(this, data) as FirOperatorCall).compose()
} else {
DummyFirStatement().compose()
}
}
override fun transformTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall, data: Nothing?): CompositeTransformResult<FirStatement> {
return if (visitTypeOperatorCall) {
(typeOperatorCall.transformChildren(this, data) as FirTypeOperatorCall).compose()
} else {
DummyFirStatement().compose()
}
}
override fun transformStringConcatenationCall(
stringConcatenationCall: FirStringConcatenationCall,
data: Nothing?
): CompositeTransformResult<FirStatement> {
return if (visitStringConcatenationCall) {
(stringConcatenationCall.transformChildren(this, data) as FirStringConcatenationCall).compose()
} else {
DummyFirStatement().compose()
}
}
override fun transformArrayOfCall(arrayOfCall: FirArrayOfCall, data: Nothing?): CompositeTransformResult<FirStatement> {
return if (visitArrayOfCall) {
(arrayOfCall.transformChildren(this, data) as FirArrayOfCall).compose()
} else {
DummyFirStatement().compose()
}
}
override fun transformFunctionCall(functionCall: FirFunctionCall, data: Nothing?): CompositeTransformResult<FirStatement> {
return if (visitFunctionCall) {
(functionCall.transformChildren(this, data) as FirFunctionCall).compose()
} else {
DummyFirStatement().compose()
}
}
override fun transformGetClassCall(getClassCall: FirGetClassCall, data: Nothing?): CompositeTransformResult<FirStatement> {
return if (visitGetClassCall) {
(getClassCall.transformChildren(this, data) as FirGetClassCall).compose()
} else {
DummyFirStatement().compose()
}
}
override fun transformBreakExpression(breakExpression: FirBreakExpression, data: Nothing?): CompositeTransformResult<FirStatement> {
return if (visitBreakExpression) {
(breakExpression.transformChildren(this, data) as FirBreakExpression).compose()
} else {
DummyFirStatement().compose()
}
}
override fun transformContinueExpression(
continueExpression: FirContinueExpression,
data: Nothing?
): CompositeTransformResult<FirStatement> {
return if (visitContinueExpression) {
(continueExpression.transformChildren(this, data) as FirContinueExpression).compose()
} else {
DummyFirStatement().compose()
}
}
override fun transformReturnExpression(returnExpression: FirReturnExpression, data: Nothing?): CompositeTransformResult<FirStatement> {
return if (visitReturnExpression) {
(returnExpression.transformChildren(this, data) as FirReturnExpression).compose()
} else {
DummyFirStatement().compose()
}
}
override fun transformThrowExpression(throwExpression: FirThrowExpression, data: Nothing?): CompositeTransformResult<FirStatement> {
return if (visitThrowExpression) {
(throwExpression.transformChildren(this, data) as FirThrowExpression).compose()
} else {
DummyFirStatement().compose()
}
}
override fun transformBlock(block: FirBlock, data: Nothing?): CompositeTransformResult<FirStatement> {
return if (!visitForLoop && block.psi is KtForExpression) {
DummyFirStatement().compose()
} else {
super.transformBlock(block, data)
}
}
override fun transformClassReferenceExpression(
classReferenceExpression: FirClassReferenceExpression,
data: Nothing?
): CompositeTransformResult<FirStatement> {
return if (visitClassReferenceExpression) {
(classReferenceExpression.transformChildren(this, data) as FirClassReferenceExpression).compose()
} else {
DummyFirStatement().compose()
}
}
override fun <T> transformConstExpression(
constExpression: FirConstExpression<T>,
data: Nothing?
): CompositeTransformResult<FirStatement> {
return if (visitConstExpression) {
(constExpression.transformChildren(this, data) as FirConstExpression<*>).compose()
} else {
DummyFirStatement().compose()
}
}
override fun transformQualifiedAccessExpression(
qualifiedAccessExpression: FirQualifiedAccessExpression,
data: Nothing?
): CompositeTransformResult<FirStatement> {
return if (visitQualifiedAccessExpression) {
(qualifiedAccessExpression.transformChildren(this, data) as FirQualifiedAccessExpression).compose()
} else {
DummyFirStatement().compose()
}
}
override fun transformCallableReferenceAccess(
callableReferenceAccess: FirCallableReferenceAccess,
data: Nothing?
): CompositeTransformResult<FirStatement> {
return if (visitCallableReferenceAccess) {
(callableReferenceAccess.transformChildren(this, data) as FirCallableReferenceAccess).compose()
} else {
DummyFirStatement().compose()
}
}
override fun transformTryExpression(tryExpression: FirTryExpression, data: Nothing?): CompositeTransformResult<FirStatement> {
return if (visitTryExpression) {
(tryExpression.transformChildren(this, data) as FirTryExpression).compose()
} else {
DummyFirStatement().compose()
}
}
override fun transformWhenExpression(whenExpression: FirWhenExpression, data: Nothing?): CompositeTransformResult<FirStatement> {
return if (visitWhenExpression) {
(whenExpression.transformChildren(this, data) as FirWhenExpression).compose()
} else {
DummyFirStatement().compose()
}
}
override fun transformNamedArgumentExpression(
namedArgumentExpression: FirNamedArgumentExpression,
data: Nothing?
): CompositeTransformResult<FirStatement> {
return if (visitNamedArgumentExpression) {
(namedArgumentExpression.transformChildren(this, data) as FirNamedArgumentExpression).compose()
} else {
DummyFirStatement().compose()
}
}
override fun transformSpreadArgumentExpression(
spreadArgumentExpression: FirSpreadArgumentExpression,
data: Nothing?
): CompositeTransformResult<FirStatement> {
return if (visitSpreadArgumentExpression) {
(spreadArgumentExpression.transformChildren(this, data) as FirSpreadArgumentExpression).compose()
} else {
DummyFirStatement().compose()
}
}
override fun transformAnonymousObject(anonymousObject: FirAnonymousObject, data: Nothing?): CompositeTransformResult<FirStatement> {
return if (visitAnonymousObject) {
(anonymousObject.transformChildren(this, data) as FirAnonymousObject).compose()
} else {
DummyFirStatement().compose()
}
}
override fun transformDoWhileLoop(doWhileLoop: FirDoWhileLoop, data: Nothing?): CompositeTransformResult<FirStatement> {
return if (visitDoWhileLoop) {
(doWhileLoop.transformChildren(this, data) as FirDoWhileLoop).compose()
} else {
DummyFirStatement().compose()
}
}
override fun transformWhileLoop(whileLoop: FirWhileLoop, data: Nothing?): CompositeTransformResult<FirStatement> {
return if (visitWhileLoop) {
(whileLoop.transformChildren(this, data) as FirWhileLoop).compose()
} else {
DummyFirStatement().compose()
}
}
override fun transformAssignment(assignment: FirAssignment, data: Nothing?): CompositeTransformResult<FirStatement> {
return if (visitAssignment) {
(assignment.transformChildren(this, data) as FirAssignment).compose()
} else {
DummyFirStatement().compose()
}
}
}

View File

@@ -9,6 +9,7 @@ import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.vfs.CharsetToolkit
import com.intellij.testFramework.TestDataPath
import com.intellij.util.PathUtil
import junit.framework.TestCase
import org.jetbrains.kotlin.fir.FirRenderer
import org.jetbrains.kotlin.fir.builder.AbstractRawFirBuilderTestCase
import org.jetbrains.kotlin.fir.lightTree.LightTree2Fir
@@ -22,10 +23,7 @@ import java.io.File
@TestDataPath("\$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners::class)
class TreesCompareTest : AbstractRawFirBuilderTestCase() {
private fun compare(
stubMode: Boolean,
visitAnnotation: Boolean = true
) {
private fun compareBase(stubMode: Boolean, compareFir: (File, LightTree2Fir) -> Boolean) {
val path = System.getProperty("user.dir")
var counter = 0
var errorCounter = 0
@@ -33,42 +31,126 @@ class TreesCompareTest : AbstractRawFirBuilderTestCase() {
val parserDefinition = KotlinParserDefinition()
val lexer = parserDefinition.createLexer(myProject)
val lightTreeConverter = LightTree2Fir(stubMode, parserDefinition, lexer)
val firVisitor = FirPartialTransformer(visitAnnotation = visitAnnotation)
println("BASE PATH: $path")
path.walkTopDown { file ->
val text = FileUtil.loadFile(file, CharsetToolkit.UTF8, true).trim()
//light tree
val firFileFromLightTree = lightTreeConverter.buildFirFile(text, file.name)
val treeFromLightTree =
StringBuilder().also { FirRenderer(it).visitFile(firVisitor.transformFile(firFileFromLightTree, null).single) }.toString()
//psi
val ktFile = createPsiFile(FileUtil.getNameWithoutExtension(PathUtil.getFileName(file.path)), text) as KtFile
val firFileFromPsi = ktFile.toFirFile(stubMode)
val treeFromPsi =
StringBuilder().also { FirRenderer(it).visitFile(firVisitor.transformFile(firFileFromPsi, null).single) }.toString()
if (treeFromLightTree != treeFromPsi) {
errorCounter++
//TestCase.assertEquals(treeFromPsi, treeFromLightTree)
}
if (!compareFir(file, lightTreeConverter)) errorCounter++
counter++
}
println("All scanned files: $counter")
println("Files that aren't equal to FIR: $errorCounter")
TestCase.assertEquals(0, errorCounter)
}
private fun compareAll(stubMode: Boolean) {
compareBase(stubMode) { file, lightTreeConverter ->
val text = FileUtil.loadFile(file, CharsetToolkit.UTF8, true).trim()
//light tree
val firFileFromLightTree = lightTreeConverter.buildFirFile(text, file.name)
val treeFromLightTree = StringBuilder().also { FirRenderer(it).visitFile(firFileFromLightTree) }.toString()
//psi
val ktFile = createPsiFile(FileUtil.getNameWithoutExtension(PathUtil.getFileName(file.path)), text) as KtFile
val firFileFromPsi = ktFile.toFirFile(stubMode)
val treeFromPsi = StringBuilder().also { FirRenderer(it).visitFile(firFileFromPsi) }.toString()
return@compareBase treeFromLightTree == treeFromPsi
}
}
private fun compare(
stubMode: Boolean,
visitAnonymousFunction: Boolean = false,
visitNamedFunction: Boolean = false,
visitMemberDeclaration: Boolean = false,
visitVariable: Boolean = false,
visitAnnotation: Boolean = false,
visitOperationCall: Boolean = false,
visitTypeOperatorCall: Boolean = false,
visitStringConcatenationCall: Boolean = false,
visitArrayOfCall: Boolean = false,
visitFunctionCall: Boolean = false,
visitGetClassCall: Boolean = false,
visitBreakExpression: Boolean = false,
visitContinueExpression: Boolean = false,
visitReturnExpression: Boolean = false,
visitThrowExpression: Boolean = false,
visitForLoop: Boolean = false,
visitClassReferenceExpression: Boolean = false,
visitConstExpression: Boolean = false,
visitQualifiedAccessExpression: Boolean = false,
visitCallableReferenceAccess: Boolean = false,
visitTryExpression: Boolean = false,
visitWhenExpression: Boolean = false,
visitNamedArgumentExpression: Boolean = false,
visitSpreadArgumentExpression: Boolean = false,
visitAnonymousObject: Boolean = false,
visitDoWhileLoop: Boolean = false,
visitWhileLoop: Boolean = false,
visitAssignment: Boolean = false
) {
val firVisitor = FirPartialTransformer(
visitAnonymousFunction = visitAnonymousFunction,
visitNamedFunction = visitNamedFunction,
visitMemberDeclaration = visitMemberDeclaration,
visitVariable = visitVariable,
visitAnnotation = visitAnnotation,
visitOperationCall = visitOperationCall,
visitTypeOperatorCall = visitTypeOperatorCall,
visitStringConcatenationCall = visitStringConcatenationCall,
visitArrayOfCall = visitArrayOfCall,
visitFunctionCall = visitFunctionCall,
visitGetClassCall = visitGetClassCall,
visitBreakExpression = visitBreakExpression,
visitContinueExpression = visitContinueExpression,
visitReturnExpression = visitReturnExpression,
visitThrowExpression = visitThrowExpression,
visitForLoop = visitForLoop,
visitClassReferenceExpression = visitClassReferenceExpression,
visitConstExpression = visitConstExpression,
visitQualifiedAccessExpression = visitQualifiedAccessExpression,
visitCallableReferenceAccess = visitCallableReferenceAccess,
visitTryExpression = visitTryExpression,
visitWhenExpression = visitWhenExpression,
visitNamedArgumentExpression = visitNamedArgumentExpression,
visitSpreadArgumentExpression = visitSpreadArgumentExpression,
visitAnonymousObject = visitAnonymousObject,
visitDoWhileLoop = visitDoWhileLoop,
visitWhileLoop = visitWhileLoop,
visitAssignment = visitAssignment
)
compareBase(stubMode) { file, lightTreeConverter ->
val text = FileUtil.loadFile(file, CharsetToolkit.UTF8, true).trim()
//light tree
val firFileFromLightTree = lightTreeConverter.buildFirFile(text, file.name)
firVisitor.transformFile(firFileFromLightTree, null)
//val treeFromLightTree = StringBuilder().also { FirRenderer(it).visitFile(firVisitor.transformFile(firFileFromLightTree, null).single) }.toString()
//psi
val ktFile = createPsiFile(FileUtil.getNameWithoutExtension(PathUtil.getFileName(file.path)), text) as KtFile
val firFileFromPsi = ktFile.toFirFile(stubMode)
firVisitor.transformFile(firFileFromPsi, null)
//val treeFromPsi = StringBuilder().also { FirRenderer(it).visitFile(firVisitor.transformFile(firFileFromPsi, null).single) }.toString()
return@compareBase firFileFromPsi.areEqualTo(firFileFromLightTree)
}
}
fun testStubCompareAll() {
compare(stubMode = true)
compareAll(stubMode = true)
}
fun testCompareAll() {
compare(stubMode = false)
compareAll(stubMode = false)
}
fun testStubCompareWithoutAnnotations() {
compare(stubMode = true, visitAnnotation = false)
}
fun testConstExpression() {
compare(stubMode = false, visitConstExpression = true, visitAnnotation = true)
}
}