Write binary and unary expressions parsing (light tree to FIR)

This commit is contained in:
Ivan Cilcic
2019-07-16 17:25:40 +03:00
committed by Mikhail Glukhikh
parent be073ce2e6
commit a13a043169
7 changed files with 495 additions and 47 deletions
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.fir.types.impl.*
import org.jetbrains.kotlin.lexer.KtToken import org.jetbrains.kotlin.lexer.KtToken
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.lexer.KtTokens.* import org.jetbrains.kotlin.lexer.KtTokens.*
import org.jetbrains.kotlin.fir.lightTree.converter.ConverterUtil.isExpression
open class BaseConverter( open class BaseConverter(
session: FirSession, session: FirSession,
@@ -25,7 +26,7 @@ open class BaseConverter(
protected val implicitAnnotationType = FirImplicitAnnotationTypeRef(session, null) protected val implicitAnnotationType = FirImplicitAnnotationTypeRef(session, null)
protected val implicitType = FirImplicitTypeRefImpl(session, null) protected val implicitType = FirImplicitTypeRefImpl(session, null)
protected fun LighterASTNode?.getChildNodesByType(type: IElementType): List<LighterASTNode> { fun LighterASTNode?.getChildNodesByType(type: IElementType): List<LighterASTNode> {
return this?.forEachChildrenReturnList { node, container -> return this?.forEachChildrenReturnList { node, container ->
when (node.tokenType) { when (node.tokenType) {
type -> container += node type -> container += node
@@ -33,7 +34,7 @@ open class BaseConverter(
} ?: listOf() } ?: listOf()
} }
protected fun LighterASTNode?.getChildrenAsArray(): Array<LighterASTNode?> { fun LighterASTNode?.getChildrenAsArray(): Array<LighterASTNode?> {
if (this == null) return arrayOf() if (this == null) return arrayOf()
val kidsRef = Ref<Array<LighterASTNode?>>() val kidsRef = Ref<Array<LighterASTNode?>>()
@@ -41,6 +42,14 @@ open class BaseConverter(
return kidsRef.get() return kidsRef.get()
} }
fun LighterASTNode.getExpressionInParentheses(): LighterASTNode {
this.forEachChildren {
if (it.isExpression()) return it
}
throw Exception()
}
protected inline fun LighterASTNode.forEachChildren(vararg skipTokens: KtToken, f: (LighterASTNode) -> Unit) { protected inline fun LighterASTNode.forEachChildren(vararg skipTokens: KtToken, f: (LighterASTNode) -> Unit) {
val kidsArray = this.getChildrenAsArray() val kidsArray = this.getChildrenAsArray()
for (kid in kidsArray) { for (kid in kidsArray) {
@@ -6,29 +6,35 @@
package org.jetbrains.kotlin.fir.lightTree.converter package org.jetbrains.kotlin.fir.lightTree.converter
import com.intellij.lang.LighterASTNode import com.intellij.lang.LighterASTNode
import com.intellij.psi.tree.IElementType
import org.jetbrains.kotlin.KtNodeType import org.jetbrains.kotlin.KtNodeType
import org.jetbrains.kotlin.KtNodeTypes import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.FirFunctionTarget import org.jetbrains.kotlin.fir.FirFunctionTarget
import org.jetbrains.kotlin.fir.FirReference
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.impl.* import org.jetbrains.kotlin.fir.declarations.impl.*
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirReturnExpression
import org.jetbrains.kotlin.fir.expressions.impl.* import org.jetbrains.kotlin.fir.expressions.impl.*
import org.jetbrains.kotlin.fir.lightTree.fir.TypeConstraint import org.jetbrains.kotlin.fir.lightTree.fir.TypeConstraint
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
import org.jetbrains.kotlin.fir.references.FirExplicitThisReference
import org.jetbrains.kotlin.fir.references.FirResolvedCallableReferenceImpl import org.jetbrains.kotlin.fir.references.FirResolvedCallableReferenceImpl
import org.jetbrains.kotlin.fir.references.FirSimpleNamedReference
import org.jetbrains.kotlin.fir.symbols.CallableId import org.jetbrains.kotlin.fir.symbols.CallableId
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.* import org.jetbrains.kotlin.fir.types.impl.*
import org.jetbrains.kotlin.lexer.KtSingleValueToken
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.parsing.KotlinExpressionParsing
import org.jetbrains.kotlin.psi.stubs.elements.KtConstantExpressionElementType import org.jetbrains.kotlin.psi.stubs.elements.KtConstantExpressionElementType
import org.jetbrains.kotlin.psi.stubs.elements.KtStringTemplateExpressionElementType import org.jetbrains.kotlin.psi.stubs.elements.KtStringTemplateExpressionElementType
import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.Variance
@@ -194,6 +200,7 @@ object ClassNameUtil {
object FunctionUtil { object FunctionUtil {
val firFunctions = mutableListOf<FirFunction>() val firFunctions = mutableListOf<FirFunction>()
val firFunctionCalls = mutableListOf<FirFunctionCall>()
fun <T> MutableList<T>.removeLast() { fun <T> MutableList<T>.removeLast() {
removeAt(size - 1) removeAt(size - 1)
@@ -15,11 +15,9 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.impl.* import org.jetbrains.kotlin.fir.declarations.impl.*
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.FirBlock
import org.jetbrains.kotlin.fir.expressions.FirDelegatedConstructorCall
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.impl.* import org.jetbrains.kotlin.fir.expressions.impl.*
import org.jetbrains.kotlin.fir.lightTree.LightTree2Fir
import org.jetbrains.kotlin.fir.lightTree.converter.ConverterUtil.extractArgumentsFrom import org.jetbrains.kotlin.fir.lightTree.converter.ConverterUtil.extractArgumentsFrom
import org.jetbrains.kotlin.fir.lightTree.converter.ConverterUtil.getAsString import org.jetbrains.kotlin.fir.lightTree.converter.ConverterUtil.getAsString
import org.jetbrains.kotlin.fir.lightTree.converter.ConverterUtil.getAsStringWithoutBacktick import org.jetbrains.kotlin.fir.lightTree.converter.ConverterUtil.getAsStringWithoutBacktick
@@ -53,7 +51,7 @@ class DeclarationsConverter(
private val stubMode: Boolean, private val stubMode: Boolean,
tree: FlyweightCapableTreeStructure<LighterASTNode> tree: FlyweightCapableTreeStructure<LighterASTNode>
) : BaseConverter(session, tree) { ) : BaseConverter(session, tree) {
private val expressionConverter = ExpressionsConverter(session, stubMode, tree) private val expressionConverter = ExpressionsConverter(session, stubMode, tree, this)
/** /**
* [org.jetbrains.kotlin.parsing.KotlinParsing.parseFile] * [org.jetbrains.kotlin.parsing.KotlinParsing.parseFile]
@@ -96,6 +94,31 @@ class DeclarationsConverter(
return firFile return firFile
} }
/**
* @see org.jetbrains.kotlin.parsing.KotlinParsing.parseBlockExpression
*/
private fun convertBlockExpression(block: LighterASTNode): FirBlock {
val firStatements = mutableListOf<FirStatement>()
block.forEachChildren {
when (it.tokenType) {
//TODO("not implemented")
BLOCK -> ""
BINARY_EXPRESSION -> firStatements += expressionConverter.convertBinaryExpression(it)
PREFIX_EXPRESSION, POSTFIX_EXPRESSION -> firStatements += expressionConverter.convertUnaryExpression(it)
else -> if (it.isExpression()) firStatements += expressionConverter.visitExpression(it)
}
}
return FirBlockImpl(session, null).apply {
firStatements.forEach { firStatement ->
if (firStatement !is FirBlock || firStatement.annotations.isNotEmpty()) {
statements += firStatement
} else {
statements += firStatement.statements
}
}
}
}
/***** PREAMBLE *****/ /***** PREAMBLE *****/
/** /**
* @see org.jetbrains.kotlin.parsing.KotlinParsing.parsePackageName * @see org.jetbrains.kotlin.parsing.KotlinParsing.parsePackageName
@@ -639,7 +662,7 @@ class DeclarationsConverter(
lateinit var identifier: String lateinit var identifier: String
val firTypeParameters = mutableListOf<FirTypeParameter>() val firTypeParameters = mutableListOf<FirTypeParameter>()
var isReturnType = false var isReturnType = false
var isDelegate = false var firDelegateExpression: FirExpression? = null
var isVar = false var isVar = false
var receiverType: FirTypeRef? = null var receiverType: FirTypeRef? = null
var returnType: FirTypeRef = implicitType var returnType: FirTypeRef = implicitType
@@ -655,7 +678,7 @@ class DeclarationsConverter(
COLON -> isReturnType = true COLON -> isReturnType = true
TYPE_REFERENCE -> if (isReturnType) returnType = convertType(it) else receiverType = convertType(it) TYPE_REFERENCE -> if (isReturnType) returnType = convertType(it) else receiverType = convertType(it)
TYPE_CONSTRAINT_LIST -> typeConstraints += convertTypeConstraints(it) TYPE_CONSTRAINT_LIST -> typeConstraints += convertTypeConstraints(it)
PROPERTY_DELEGATE -> isDelegate = true PROPERTY_DELEGATE -> firDelegateExpression = expressionConverter.visitExpression(it)
VAR_KEYWORD -> isVar = true VAR_KEYWORD -> isVar = true
PROPERTY_ACCESSOR -> { PROPERTY_ACCESSOR -> {
val propertyAccessor = convertGetterOrSetter(it, returnType) val propertyAccessor = convertGetterOrSetter(it, returnType)
@@ -675,11 +698,7 @@ class DeclarationsConverter(
returnType, returnType,
isVar, isVar,
firExpression, firExpression,
delegate = if (isDelegate) { delegate = firDelegateExpression
FirExpressionStub(session, null)
//TODO("not implemented")
//{ property.delegate?.expression }.toFirExpression("Should have delegate")
} else null
) )
} else { } else {
FirMemberPropertyImpl( FirMemberPropertyImpl(
@@ -700,11 +719,7 @@ class DeclarationsConverter(
firExpression, firExpression,
getter ?: FirDefaultPropertyGetter(session, null, returnType, modifiers.getVisibility()), getter ?: FirDefaultPropertyGetter(session, null, returnType, modifiers.getVisibility()),
if (isVar) setter ?: FirDefaultPropertySetter(session, null, returnType, modifiers.getVisibility()) else null, if (isVar) setter ?: FirDefaultPropertySetter(session, null, returnType, modifiers.getVisibility()) else null,
if (isDelegate) { firDelegateExpression
FirExpressionStub(session, null)
//TODO("not implemented")
//{ property.delegate?.expression }.toFirExpression("Should have delegate")
} else null
).apply { ).apply {
this.typeParameters += firTypeParameters this.typeParameters += firTypeParameters
this.joinTypeParameters(typeConstraints) this.joinTypeParameters(typeConstraints)
@@ -854,6 +869,7 @@ class DeclarationsConverter(
/** /**
* @see org.jetbrains.kotlin.parsing.KotlinParsing.parseFunctionBody * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseFunctionBody
* @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.buildFirBody
*/ */
private fun visitFunctionBody(firBlock: FirBlock?, firExpression: FirExpression?): FirBlock? { private fun visitFunctionBody(firBlock: FirBlock?, firExpression: FirExpression?): FirBlock? {
return when { return when {
@@ -878,11 +894,10 @@ class DeclarationsConverter(
/** /**
* @see org.jetbrains.kotlin.parsing.KotlinParsing.parseBlock * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseBlock
*/ */
private fun visitBlock(block: LighterASTNode?): FirBlock { private fun visitBlock(block: LighterASTNode): FirBlock {
return if (!stubMode) { return if (!stubMode) {
//TODO("not implemented") val blockTree = LightTree2Fir.buildLightTreeBlockExpression(block.getAsString())
FirBlockImpl(session, null) return DeclarationsConverter(session, stubMode, blockTree).convertBlockExpression(blockTree.root)
//visitStatements(ctx.statements())
} else { } else {
FirSingleExpressionBlock( FirSingleExpressionBlock(
session, session,
@@ -1116,7 +1131,7 @@ class DeclarationsConverter(
/** /**
* @see org.jetbrains.kotlin.parsing.KotlinParsing.parseTypeArgumentList * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseTypeArgumentList
*/ */
private fun convertTypeArguments(typeArguments: LighterASTNode): List<FirTypeProjection> { fun convertTypeArguments(typeArguments: LighterASTNode): List<FirTypeProjection> {
return typeArguments.forEachChildrenReturnList { node, container -> return typeArguments.forEachChildrenReturnList { node, container ->
when (node.tokenType) { when (node.tokenType) {
TYPE_PROJECTION -> container += convertTypeProjection(node) TYPE_PROJECTION -> container += convertTypeProjection(node)
@@ -8,39 +8,238 @@ package org.jetbrains.kotlin.fir.lightTree.converter
import com.intellij.lang.LighterASTNode import com.intellij.lang.LighterASTNode
import com.intellij.util.diff.FlyweightCapableTreeStructure import com.intellij.util.diff.FlyweightCapableTreeStructure
import org.jetbrains.kotlin.KtNodeTypes.* import org.jetbrains.kotlin.KtNodeTypes.*
import org.jetbrains.kotlin.fir.FirReference
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.builder.parseCharacter import org.jetbrains.kotlin.fir.builder.*
import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.impl.* import org.jetbrains.kotlin.fir.expressions.impl.*
import org.jetbrains.kotlin.fir.lightTree.converter.ConverterUtil.getAsString import org.jetbrains.kotlin.fir.lightTree.converter.ConverterUtil.getAsString
import org.jetbrains.kotlin.fir.lightTree.converter.ConverterUtil.isExpression import org.jetbrains.kotlin.fir.lightTree.converter.ConverterUtil.isExpression
import org.jetbrains.kotlin.fir.lightTree.converter.ConverterUtil.nameAsSafeName import org.jetbrains.kotlin.fir.lightTree.converter.ConverterUtil.nameAsSafeName
import org.jetbrains.kotlin.fir.lightTree.converter.ConverterUtil.getAsStringUnescapedValue import org.jetbrains.kotlin.fir.lightTree.converter.ConverterUtil.getAsStringUnescapedValue
import org.jetbrains.kotlin.fir.lightTree.converter.ConverterUtil.extractArgumentsFrom
import org.jetbrains.kotlin.fir.lightTree.converter.FunctionUtil.removeLast
import org.jetbrains.kotlin.fir.lightTree.converter.utils.*
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
import org.jetbrains.kotlin.fir.references.FirSimpleNamedReference
import org.jetbrains.kotlin.fir.types.FirTypeProjection
import org.jetbrains.kotlin.ir.expressions.IrConstKind import org.jetbrains.kotlin.ir.expressions.IrConstKind
import org.jetbrains.kotlin.lexer.KtTokens.* import org.jetbrains.kotlin.lexer.KtTokens.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.stubs.elements.KtConstantExpressionElementType import org.jetbrains.kotlin.psi.stubs.elements.KtConstantExpressionElementType
import org.jetbrains.kotlin.resolve.constants.evaluate.* import org.jetbrains.kotlin.resolve.constants.evaluate.*
import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.util.OperatorNameConventions
class ExpressionsConverter( class ExpressionsConverter(
private val session: FirSession, val session: FirSession,
private val stubMode: Boolean, private val stubMode: Boolean,
tree: FlyweightCapableTreeStructure<LighterASTNode> tree: FlyweightCapableTreeStructure<LighterASTNode>,
private val declarationsConverter: DeclarationsConverter
) : BaseConverter(session, tree) { ) : BaseConverter(session, tree) {
/***** EXPRESSIONS *****/ /***** EXPRESSIONS *****/
fun visitExpression(expression: LighterASTNode): FirExpression { fun visitExpression(expression: LighterASTNode): FirExpression {
if (!stubMode) { if (!stubMode) {
when (expression.tokenType) { when (expression.tokenType) {
BINARY_EXPRESSION -> return convertBinaryExpression(expression) as FirExpression
PREFIX_EXPRESSION, POSTFIX_EXPRESSION -> return convertUnaryExpression(expression) as FirExpression
in qualifiedAccessTokens -> return convertQualifiedExpression(expression)
CALL_EXPRESSION -> return convertCallExpression(expression)
ARRAY_ACCESS_EXPRESSION -> return convertArrayAccessExpression(expression)
STRING_TEMPLATE -> return convertStringTemplate(expression) STRING_TEMPLATE -> return convertStringTemplate(expression)
is KtConstantExpressionElementType -> return convertConstantExpression(expression) is KtConstantExpressionElementType -> return convertConstantExpression(expression)
REFERENCE_EXPRESSION -> return convertSimpleNameExpression(expression)
PARENTHESIZED, PROPERTY_DELEGATE, INDICES -> return visitExpression(expression.getExpressionInParentheses())
} }
//TODO("not implemented") return FirExpressionStub(session, null)
//TODO("not fully implemented")
} }
return FirExpressionStub(session, null) return FirExpressionStub(session, null)
} }
/** /**
* @see org.jetbrains.kotlin.parsing.KotlinExpressionParsing.parseBinaryExpression
* @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.visitBinaryExpression
*/
fun convertBinaryExpression(binaryExpression: LighterASTNode): FirStatement {
var isLeftArgument = true
lateinit var operationTokenName: String
lateinit var leftArgNode: LighterASTNode
lateinit var rightArgAsFir: FirExpression
binaryExpression.forEachChildren {
when (it.tokenType) {
OPERATION_REFERENCE -> {
isLeftArgument = false
operationTokenName = it.getAsString()
}
else -> if (it.isExpression()) {
if (isLeftArgument) {
leftArgNode = it
} else {
rightArgAsFir = visitExpression(it)
}
}
}
}
val operationToken = operationTokenName.getOperationSymbol()
when (operationToken) {
ELVIS ->
return visitExpression(leftArgNode).generateNotNullOrOther(session, rightArgAsFir, "elvis", null)
ANDAND, OROR ->
return visitExpression(leftArgNode).generateLazyLogicalOperation(session, rightArgAsFir, operationToken == ANDAND, null)
in OperatorConventions.IN_OPERATIONS ->
return rightArgAsFir.generateContainsOperation(session, visitExpression(leftArgNode), operationToken == NOT_IN, null, null)
}
val conventionCallName = operationToken.toBinaryName()
return if (conventionCallName != null || operationToken == IDENTIFIER) {
FirFunctionCallImpl(session, null).apply {
calleeReference = FirSimpleNamedReference(
this@ExpressionsConverter.session, null,
conventionCallName ?: operationTokenName.nameAsSafeName()
)
explicitReceiver = visitExpression(leftArgNode)
arguments += rightArgAsFir
}
} else {
val firOperation = operationToken.toFirOperation()
if (firOperation in FirOperation.ASSIGNMENTS) {
return convertAssignment(leftArgNode, rightArgAsFir, firOperation)
} else {
FirOperatorCallImpl(session, null, firOperation).apply {
arguments += visitExpression(leftArgNode)
arguments += rightArgAsFir
}
}
}
}
/**
* @see org.jetbrains.kotlin.parsing.KotlinExpressionParsing.parsePostfixExpression
* @see org.jetbrains.kotlin.parsing.KotlinExpressionParsing.parsePrefixExpression
* @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.visitUnaryExpression
*/
fun convertUnaryExpression(unaryExpression: LighterASTNode): FirStatement {
lateinit var operationTokenName: String
lateinit var argument: LighterASTNode
unaryExpression.forEachChildren {
when (it.tokenType) {
OPERATION_REFERENCE -> operationTokenName = it.getAsString()
else -> if (it.isExpression()) argument = it
}
}
val operationToken = operationTokenName.getOperationSymbol()
if (operationToken == EXCLEXCL) {
return bangBangToWhen(session, visitExpression(argument))
}
val conventionCallName = operationToken.toUnaryName()
return if (conventionCallName != null) {
if (operationToken in OperatorConventions.INCREMENT_OPERATIONS) {
return generateIncrementOrDecrementBlock(
argument,
callName = conventionCallName,
prefix = unaryExpression.tokenType == PREFIX_EXPRESSION
)
}
FirFunctionCallImpl(session, null).apply {
calleeReference = FirSimpleNamedReference(this@ExpressionsConverter.session, null, conventionCallName)
explicitReceiver = visitExpression(argument)
}
} else {
val firOperation = operationToken.toFirOperation()
FirOperatorCallImpl(session, null, firOperation).apply {
arguments += visitExpression(argument)
}
}
}
/**
* @see org.jetbrains.kotlin.parsing.KotlinExpressionParsing.parsePostfixExpression
* @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.visitQualifiedExpression
*/
private fun convertQualifiedExpression(dotQualifiedExpression: LighterASTNode): FirExpression {
var isSelector = false
var isSafe = false
var firSelector: FirExpression? = null //after dot
lateinit var firReceiver: FirExpression //before dot
dotQualifiedExpression.forEachChildren {
when (it.tokenType) {
DOT -> isSelector = true
SAFE_ACCESS -> {
isSafe = true
isSelector = true
}
else -> if (isSelector) firSelector = visitExpression(it) else firReceiver = visitExpression(it)
}
}
if (firSelector == null) {
return FirErrorExpressionImpl(session, null, "Qualified expression without selector")
}
//TODO use contracts?
if (firSelector is FirModifiableQualifiedAccess<*>) {
(firSelector as FirModifiableQualifiedAccess<*>).safe = isSafe
(firSelector as FirModifiableQualifiedAccess<*>).explicitReceiver = firReceiver
}
return firSelector!!
}
/**
* @see org.jetbrains.kotlin.parsing.KotlinExpressionParsing.parseCallSuffix
*/
private fun convertCallExpression(callSuffix: LighterASTNode): FirExpression {
var name: String? = null
val firTypeArguments = mutableListOf<FirTypeProjection>()
val firValueArguments = mutableListOf<FirExpression>()
var additionalArgument: FirExpression? = null
callSuffix.forEachChildren {
when (it.tokenType) {
REFERENCE_EXPRESSION -> name = it.getAsString()
TYPE_ARGUMENT_LIST -> firTypeArguments += declarationsConverter.convertTypeArguments(it)
VALUE_ARGUMENT_LIST -> firValueArguments += convertValueArguments(it)
LAMBDA_ARGUMENT -> firValueArguments += convertAnnotatedLambda(it)
else -> additionalArgument = visitExpression(it)
}
}
return FirFunctionCallImpl(session, null).apply {
val calleeReference = when {
name != null -> FirSimpleNamedReference(this@ExpressionsConverter.session, null, name.nameAsSafeName())
additionalArgument != null -> {
arguments += additionalArgument!!
FirSimpleNamedReference(this@ExpressionsConverter.session, null, OperatorNameConventions.INVOKE)
}
else -> FirErrorNamedReference(this@ExpressionsConverter.session, null, "Call has no callee")
}
this.calleeReference = calleeReference
FunctionUtil.firFunctionCalls += this
this.extractArgumentsFrom(firValueArguments, stubMode)
typeArguments += firTypeArguments
FunctionUtil.firFunctionCalls.removeLast()
}
}
/**
* @see org.jetbrains.kotlin.parsing.KotlinExpressionParsing.parseAnnotatedLambda
*/
private fun convertAnnotatedLambda(annotatedLambda: LighterASTNode): FirExpression {
annotatedLambda.forEachChildren {
when (it.tokenType) {
LAMBDA_EXPRESSION -> ""
LABELED_EXPRESSION -> ""
ANNOTATED_EXPRESSION -> ""
}
}
return FirExpressionStub(session, null)
}
/**
* @see org.jetbrains.kotlin.parsing.KotlinExpressionParsing.parseStringTemplate
* @see org.jetbrains.kotlin.fir.builder.ConversionUtilsKt.toInterpolatingCall * @see org.jetbrains.kotlin.fir.builder.ConversionUtilsKt.toInterpolatingCall
*/ */
private fun convertStringTemplate(stringTemplate: LighterASTNode): FirExpression { private fun convertStringTemplate(stringTemplate: LighterASTNode): FirExpression {
@@ -94,6 +293,7 @@ class ExpressionsConverter(
} }
/** /**
* @see org.jetbrains.kotlin.parsing.KotlinExpressionParsing.parseLiteralConstant
* @see org.jetbrains.kotlin.fir.builder.ConversionUtilsKt.generateConstantExpressionByLiteral * @see org.jetbrains.kotlin.fir.builder.ConversionUtilsKt.generateConstantExpressionByLiteral
*/ */
private fun convertConstantExpression(constantExpression: LighterASTNode): FirExpression { private fun convertConstantExpression(constantExpression: LighterASTNode): FirExpression {
@@ -143,6 +343,39 @@ class ExpressionsConverter(
} }
} }
/**
* @see org.jetbrains.kotlin.parsing.KotlinExpressionParsing.parseArrayAccess
* @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.visitArrayAccessExpression
*/
private fun convertArrayAccessExpression(arrayAccess: LighterASTNode): FirFunctionCall {//Pair<LighterASTNode, List<FirExpression>> {
lateinit var expression: FirExpression
val indexes: MutableList<FirExpression> = mutableListOf()
arrayAccess.forEachChildren {
when (it.tokenType) {
INDICES -> indexes += visitExpression(it)
else -> if (it.isExpression()) expression = visitExpression(it)
}
}
return FirFunctionCallImpl(session, null).apply {
calleeReference = FirSimpleNamedReference(this@ExpressionsConverter.session, null, OperatorNameConventions.GET)
explicitReceiver = expression
for (indexExpression in indexes) {
arguments += indexExpression
}
}
}
/**
* @see org.jetbrains.kotlin.parsing.KotlinExpressionParsing.parseSimpleNameExpression
* @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.visitSimpleNameExpression
*/
private fun convertSimpleNameExpression(referenceExpression: LighterASTNode): FirQualifiedAccessExpression {
return FirQualifiedAccessExpressionImpl(session, null).apply {
calleeReference =
FirSimpleNamedReference(this@ExpressionsConverter.session, null, referenceExpression.getAsString().nameAsSafeName())
}
}
/** /**
* @see org.jetbrains.kotlin.parsing.KotlinExpressionParsing.parseValueArgumentList * @see org.jetbrains.kotlin.parsing.KotlinExpressionParsing.parseValueArgumentList
*/ */
@@ -177,4 +410,21 @@ class ExpressionsConverter(
else -> firExpression else -> firExpression
} }
} }
fun convertLValue(leftArgNode: LighterASTNode, container: FirModifiableQualifiedAccess<*>): FirReference {
leftArgNode.forEachChildren {
return when (it.tokenType) {
//THIS_EXPRESSION -> FirExplicitThisReference(session, left, left.getLabelName())
REFERENCE_EXPRESSION -> FirSimpleNamedReference(session, null, it.getAsString().nameAsSafeName())
in qualifiedAccessTokens -> (visitExpression(it) as FirQualifiedAccess).let { firQualifiedAccess ->
container.explicitReceiver = firQualifiedAccess.explicitReceiver
container.safe = firQualifiedAccess.safe
return@let firQualifiedAccess.calleeReference
}
PARENTHESIZED -> convertLValue(it.getExpressionInParentheses(), container)
else -> FirErrorNamedReference(session, null, "Unsupported LValue: ${leftArgNode.tokenType}")
}
}
return FirErrorNamedReference(session, null, "Unsupported LValue: ${leftArgNode.tokenType}")
}
} }
@@ -0,0 +1,161 @@
/*
* 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.converter.utils
import com.intellij.lang.LighterASTNode
import com.intellij.psi.tree.IElementType
import com.intellij.psi.tree.TokenSet
import org.jetbrains.kotlin.KtNodeTypes.*
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.builder.*
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.impl.*
import org.jetbrains.kotlin.fir.lightTree.converter.ConverterUtil.getAsString
import org.jetbrains.kotlin.fir.lightTree.converter.ConverterUtil.nameAsSafeName
import org.jetbrains.kotlin.fir.lightTree.converter.ExpressionsConverter
import org.jetbrains.kotlin.fir.references.FirSimpleNamedReference
import org.jetbrains.kotlin.lexer.KtSingleValueToken
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.lexer.KtTokens.DOT
import org.jetbrains.kotlin.lexer.KtTokens.SAFE_ACCESS
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.parsing.KotlinExpressionParsing
import org.jetbrains.kotlin.util.OperatorNameConventions
val qualifiedAccessTokens = TokenSet.create(DOT_QUALIFIED_EXPRESSION, SAFE_ACCESS_EXPRESSION)
fun String.getOperationSymbol(): IElementType {
KotlinExpressionParsing.ALL_OPERATIONS.types.forEach {
if (it is KtSingleValueToken && it.value == this) return it
}
return KtTokens.IDENTIFIER
}
fun ExpressionsConverter.convertAssignment(
leftArgNode: LighterASTNode,
rightArgAsFir: FirExpression,
operation: FirOperation
): FirStatement {
if (leftArgNode.tokenType == PARENTHESIZED) {
return convertAssignment(leftArgNode.getExpressionInParentheses(), rightArgAsFir, operation)
}
if (leftArgNode.tokenType == ARRAY_ACCESS_EXPRESSION) {
val arrayAccessFunctionCall = visitExpression(leftArgNode) as FirFunctionCall
val firArrayExpression = arrayAccessFunctionCall.explicitReceiver!!
val arraySet = if (operation != FirOperation.ASSIGN) {
FirArraySetCallImpl(session, null, rightArgAsFir, operation).apply {
indexes += arrayAccessFunctionCall.arguments
}
} else {
return FirFunctionCallImpl(session, null).apply {
calleeReference = FirSimpleNamedReference(this@convertAssignment.session, null, OperatorNameConventions.SET)
explicitReceiver = firArrayExpression
arguments += arrayAccessFunctionCall.arguments
arguments += rightArgAsFir
}
}
if (leftArgNode.getChildNodesByType(REFERENCE_EXPRESSION).isNotEmpty()) {
return arraySet.apply {
lValue = (firArrayExpression as FirQualifiedAccess).calleeReference
}
}
return FirBlockImpl(this@convertAssignment.session, null).apply {
val name = Name.special("<array-set>")
statements += generateTemporaryVariable(this@convertAssignment.session, null, name, firArrayExpression)
statements += arraySet.apply { lValue = FirSimpleNamedReference(this@convertAssignment.session, null, name) }
}
}
if (operation != FirOperation.ASSIGN &&
leftArgNode.tokenType != REFERENCE_EXPRESSION && leftArgNode.tokenType != THIS_EXPRESSION &&
(leftArgNode.tokenType !in qualifiedAccessTokens || getSelectorType(leftArgNode.getChildrenAsArray()) != REFERENCE_EXPRESSION)
) {
return FirBlockImpl(session, null).apply {
val name = Name.special("<complex-set>")
statements += generateTemporaryVariable(this@convertAssignment.session, null, name, visitExpression(leftArgNode))
statements += FirVariableAssignmentImpl(this@convertAssignment.session, null, rightArgAsFir, operation).apply {
lValue = FirSimpleNamedReference(this@convertAssignment.session, null, name)
}
}
}
return FirVariableAssignmentImpl(session, null, rightArgAsFir, operation).apply {
lValue = convertLValue(leftArgNode, this)
}
}
fun ExpressionsConverter.generateIncrementOrDecrementBlock(
argument: LighterASTNode?,
callName: Name,
prefix: Boolean
): FirExpression {
if (argument == null) {
return FirErrorExpressionImpl(session, null, "Inc/dec without operand")
}
return FirBlockImpl(session, null).apply {
val tempName = Name.special("<unary>")
val temporaryVariable = generateTemporaryVariable(this@generateIncrementOrDecrementBlock.session, null, tempName, visitExpression(argument))
statements += temporaryVariable
val resultName = Name.special("<unary-result>")
val resultInitializer = FirFunctionCallImpl(this@generateIncrementOrDecrementBlock.session, null).apply {
this.calleeReference = FirSimpleNamedReference(this@generateIncrementOrDecrementBlock.session, null, callName)
this.explicitReceiver = generateResolvedAccessExpression(this@generateIncrementOrDecrementBlock.session, null, temporaryVariable)
}
val resultVar = generateTemporaryVariable(this@generateIncrementOrDecrementBlock.session, null, resultName, resultInitializer)
val assignment = convertAssignment(
argument,
if (prefix && argument.tokenType != REFERENCE_EXPRESSION)
generateResolvedAccessExpression(this@generateIncrementOrDecrementBlock.session, null, resultVar)
else
resultInitializer,
FirOperation.ASSIGN
)
fun appendAssignment() {
if (assignment is FirBlock) {
statements += assignment.statements
} else {
statements += assignment
}
}
if (prefix) {
if (argument.tokenType != REFERENCE_EXPRESSION) {
statements += resultVar
appendAssignment()
statements += generateResolvedAccessExpression(this@generateIncrementOrDecrementBlock.session, null, resultVar)
} else {
appendAssignment()
statements += generateAccessExpression(this@generateIncrementOrDecrementBlock.session, null, argument.getAsString().nameAsSafeName())
}
} else {
appendAssignment()
statements += generateResolvedAccessExpression(this@generateIncrementOrDecrementBlock.session, null, temporaryVariable)
}
}
}
fun bangBangToWhen(session: FirSession, baseExpression: FirExpression): FirWhenExpression {
return baseExpression.generateNotNullOrOther(
session,
FirThrowExpressionImpl(
session, null, FirFunctionCallImpl(session, null).apply {
calleeReference = FirSimpleNamedReference(session, null, RawFirBuilder.KNPE)
}
), "bangbang", null
)
}
private fun getSelectorType(qualifiedAccessChildren: Array<LighterASTNode?>): IElementType? {
var isSelector = false
qualifiedAccessChildren.forEach {
if (it == null) return null
when (it.tokenType) {
DOT, SAFE_ACCESS -> isSelector = true
else -> if (isSelector) return it.tokenType
}
}
return null
}
@@ -7,11 +7,17 @@ package org.jetbrains.kotlin.fir.lightTree.compare
import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.vfs.CharsetToolkit import com.intellij.openapi.vfs.CharsetToolkit
import com.intellij.psi.PsiFileFactory
import com.intellij.psi.PsiManager
import com.intellij.psi.impl.PsiFileFactoryImpl
import com.intellij.psi.impl.PsiManagerImpl
import com.intellij.testFramework.TestDataPath import com.intellij.testFramework.TestDataPath
import com.intellij.util.PathUtil import com.intellij.util.PathUtil
import junit.framework.TestCase import junit.framework.TestCase
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirRenderer import org.jetbrains.kotlin.fir.FirRenderer
import org.jetbrains.kotlin.fir.builder.AbstractRawFirBuilderTestCase import org.jetbrains.kotlin.fir.builder.AbstractRawFirBuilderTestCase
import org.jetbrains.kotlin.fir.builder.RawFirBuilder
import org.jetbrains.kotlin.fir.lightTree.LightTree2Fir import org.jetbrains.kotlin.fir.lightTree.LightTree2Fir
import org.jetbrains.kotlin.fir.lightTree.walkTopDown import org.jetbrains.kotlin.fir.lightTree.walkTopDown
import org.jetbrains.kotlin.parsing.KotlinParserDefinition import org.jetbrains.kotlin.parsing.KotlinParserDefinition
@@ -23,7 +29,7 @@ import java.io.File
@TestDataPath("\$PROJECT_ROOT") @TestDataPath("\$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners::class) @RunWith(JUnit3RunnerWithInners::class)
class TreesCompareTest : AbstractRawFirBuilderTestCase() { class TreesCompareTest : AbstractRawFirBuilderTestCase() {
private fun compareBase(stubMode: Boolean, compareFir: (File) -> Boolean) { private fun compareBase(compareFir: (File) -> Boolean) {
val path = System.getProperty("user.dir") val path = System.getProperty("user.dir")
var counter = 0 var counter = 0
var errorCounter = 0 var errorCounter = 0
@@ -40,7 +46,7 @@ class TreesCompareTest : AbstractRawFirBuilderTestCase() {
private fun compareAll(stubMode: Boolean) { private fun compareAll(stubMode: Boolean) {
val lightTreeConverter = LightTree2Fir(stubMode, myProject) val lightTreeConverter = LightTree2Fir(stubMode, myProject)
compareBase(stubMode) { file -> compareBase { file ->
val text = FileUtil.loadFile(file, CharsetToolkit.UTF8, true).trim() val text = FileUtil.loadFile(file, CharsetToolkit.UTF8, true).trim()
//light tree //light tree
@@ -122,7 +128,7 @@ class TreesCompareTest : AbstractRawFirBuilderTestCase() {
visitAssignment = visitAssignment visitAssignment = visitAssignment
) )
val lightTreeConverter = LightTree2Fir(stubMode, myProject) val lightTreeConverter = LightTree2Fir(stubMode, myProject)
compareBase(stubMode) { file -> compareBase { file ->
val text = FileUtil.loadFile(file, CharsetToolkit.UTF8, true).trim() val text = FileUtil.loadFile(file, CharsetToolkit.UTF8, true).trim()
//light tree //light tree
@@ -149,15 +149,15 @@ internal fun generateConstantExpressionByLiteral(expression: KtConstantExpressio
} }
internal fun IElementType.toBinaryName(): Name? { fun IElementType.toBinaryName(): Name? {
return OperatorConventions.BINARY_OPERATION_NAMES[this] return OperatorConventions.BINARY_OPERATION_NAMES[this]
} }
internal fun IElementType.toUnaryName(): Name? { fun IElementType.toUnaryName(): Name? {
return OperatorConventions.UNARY_OPERATION_NAMES[this] return OperatorConventions.UNARY_OPERATION_NAMES[this]
} }
internal fun IElementType.toFirOperation(): FirOperation = fun IElementType.toFirOperation(): FirOperation =
when (this) { when (this) {
KtTokens.LT -> FirOperation.LT KtTokens.LT -> FirOperation.LT
KtTokens.GT -> FirOperation.GT KtTokens.GT -> FirOperation.GT
@@ -181,8 +181,8 @@ internal fun IElementType.toFirOperation(): FirOperation =
else -> throw AssertionError(this.toString()) else -> throw AssertionError(this.toString())
} }
internal fun FirExpression.generateNotNullOrOther( fun FirExpression.generateNotNullOrOther(
session: FirSession, other: FirExpression, caseId: String, basePsi: KtElement session: FirSession, other: FirExpression, caseId: String, basePsi: KtElement?
): FirWhenExpression { ): FirWhenExpression {
val subjectName = Name.special("<$caseId>") val subjectName = Name.special("<$caseId>")
val subjectVariable = generateTemporaryVariable(session, basePsi, subjectName, this) val subjectVariable = generateTemporaryVariable(session, basePsi, subjectName, this)
@@ -209,8 +209,8 @@ internal fun FirExpression.generateNotNullOrOther(
} }
} }
internal fun FirExpression.generateLazyLogicalOperation( fun FirExpression.generateLazyLogicalOperation(
other: FirExpression, isAnd: Boolean, basePsi: KtElement other: FirExpression, isAnd: Boolean, basePsi: KtElement?
): FirWhenExpression { ): FirWhenExpression {
val terminalExpression = FirConstExpressionImpl(psi, IrConstKind.Boolean, !isAnd) val terminalExpression = FirConstExpressionImpl(psi, IrConstKind.Boolean, !isAnd)
val terminalBlock = FirSingleExpressionBlock(terminalExpression) val terminalBlock = FirSingleExpressionBlock(terminalExpression)
@@ -328,11 +328,11 @@ internal fun Array<KtStringTemplateEntry>.toInterpolatingCall(
return if (hasExpressions) result!! else FirConstExpressionImpl(base, IrConstKind.String, sb.toString()) return if (hasExpressions) result!! else FirConstExpressionImpl(base, IrConstKind.String, sb.toString())
} }
internal fun FirExpression.generateContainsOperation( fun FirExpression.generateContainsOperation(
argument: FirExpression, argument: FirExpression,
inverted: Boolean, inverted: Boolean,
base: KtExpression?, base: KtExpression?,
operationReference: KtOperationReferenceExpression operationReference: KtOperationReferenceExpression?
): FirFunctionCall { ): FirFunctionCall {
val containsCall = FirFunctionCallImpl(base).apply { val containsCall = FirFunctionCallImpl(base).apply {
calleeReference = FirSimpleNamedReference(operationReference, OperatorNameConventions.CONTAINS) calleeReference = FirSimpleNamedReference(operationReference, OperatorNameConventions.CONTAINS)
@@ -425,12 +425,12 @@ internal fun generateIncrementOrDecrementBlock(
} }
} }
internal fun generateAccessExpression(psi: PsiElement?, name: Name): FirQualifiedAccessExpression = fun generateAccessExpression(psi: PsiElement?, name: Name): FirQualifiedAccessExpression =
FirQualifiedAccessExpressionImpl(psi).apply { FirQualifiedAccessExpressionImpl(psi).apply {
calleeReference = FirSimpleNamedReference(psi, name) calleeReference = FirSimpleNamedReference(psi, name)
} }
internal fun generateResolvedAccessExpression(psi: PsiElement?, variable: FirVariable<*>): FirQualifiedAccessExpression = fun generateResolvedAccessExpression(psi: PsiElement?, variable: FirVariable<*>): FirQualifiedAccessExpression =
FirQualifiedAccessExpressionImpl(psi).apply { FirQualifiedAccessExpressionImpl(psi).apply {
calleeReference = FirResolvedCallableReferenceImpl(psi, variable.name, variable.symbol) calleeReference = FirResolvedCallableReferenceImpl(psi, variable.name, variable.symbol)
} }
@@ -462,7 +462,7 @@ internal fun generateDestructuringBlock(
} }
} }
internal fun generateTemporaryVariable( fun generateTemporaryVariable(
session: FirSession, psi: PsiElement?, name: Name, initializer: FirExpression session: FirSession, psi: PsiElement?, name: Name, initializer: FirExpression
): FirVariable<*> = ): FirVariable<*> =
FirVariableImpl(session, psi, name, FirImplicitTypeRefImpl(psi), false, initializer, FirVariableSymbol(name)).apply { FirVariableImpl(session, psi, name, FirImplicitTypeRefImpl(psi), false, initializer, FirVariableSymbol(name)).apply {