Write binary and unary expressions parsing (light tree to FIR)
This commit is contained in:
committed by
Mikhail Glukhikh
parent
be073ce2e6
commit
a13a043169
+11
-2
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.fir.types.impl.*
|
||||
import org.jetbrains.kotlin.lexer.KtToken
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.lexer.KtTokens.*
|
||||
import org.jetbrains.kotlin.fir.lightTree.converter.ConverterUtil.isExpression
|
||||
|
||||
open class BaseConverter(
|
||||
session: FirSession,
|
||||
@@ -25,7 +26,7 @@ open class BaseConverter(
|
||||
protected val implicitAnnotationType = FirImplicitAnnotationTypeRef(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 ->
|
||||
when (node.tokenType) {
|
||||
type -> container += node
|
||||
@@ -33,7 +34,7 @@ open class BaseConverter(
|
||||
} ?: listOf()
|
||||
}
|
||||
|
||||
protected fun LighterASTNode?.getChildrenAsArray(): Array<LighterASTNode?> {
|
||||
fun LighterASTNode?.getChildrenAsArray(): Array<LighterASTNode?> {
|
||||
if (this == null) return arrayOf()
|
||||
|
||||
val kidsRef = Ref<Array<LighterASTNode?>>()
|
||||
@@ -41,6 +42,14 @@ open class BaseConverter(
|
||||
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) {
|
||||
val kidsArray = this.getChildrenAsArray()
|
||||
for (kid in kidsArray) {
|
||||
|
||||
+10
-3
@@ -6,29 +6,35 @@
|
||||
package org.jetbrains.kotlin.fir.lightTree.converter
|
||||
|
||||
import com.intellij.lang.LighterASTNode
|
||||
import com.intellij.psi.tree.IElementType
|
||||
import org.jetbrains.kotlin.KtNodeType
|
||||
import org.jetbrains.kotlin.KtNodeTypes
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.fir.FirFunctionTarget
|
||||
import org.jetbrains.kotlin.fir.FirReference
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.*
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirReturnExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.expressions.impl.*
|
||||
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.FirSimpleNamedReference
|
||||
import org.jetbrains.kotlin.fir.symbols.CallableId
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
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.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
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.KtStringTemplateExpressionElementType
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
@@ -194,6 +200,7 @@ object ClassNameUtil {
|
||||
|
||||
object FunctionUtil {
|
||||
val firFunctions = mutableListOf<FirFunction>()
|
||||
val firFunctionCalls = mutableListOf<FirFunctionCall>()
|
||||
|
||||
fun <T> MutableList<T>.removeLast() {
|
||||
removeAt(size - 1)
|
||||
|
||||
+37
-22
@@ -15,11 +15,9 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.*
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
|
||||
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.*
|
||||
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.getAsString
|
||||
import org.jetbrains.kotlin.fir.lightTree.converter.ConverterUtil.getAsStringWithoutBacktick
|
||||
@@ -53,7 +51,7 @@ class DeclarationsConverter(
|
||||
private val stubMode: Boolean,
|
||||
tree: FlyweightCapableTreeStructure<LighterASTNode>
|
||||
) : BaseConverter(session, tree) {
|
||||
private val expressionConverter = ExpressionsConverter(session, stubMode, tree)
|
||||
private val expressionConverter = ExpressionsConverter(session, stubMode, tree, this)
|
||||
|
||||
/**
|
||||
* [org.jetbrains.kotlin.parsing.KotlinParsing.parseFile]
|
||||
@@ -96,6 +94,31 @@ class DeclarationsConverter(
|
||||
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 *****/
|
||||
/**
|
||||
* @see org.jetbrains.kotlin.parsing.KotlinParsing.parsePackageName
|
||||
@@ -639,7 +662,7 @@ class DeclarationsConverter(
|
||||
lateinit var identifier: String
|
||||
val firTypeParameters = mutableListOf<FirTypeParameter>()
|
||||
var isReturnType = false
|
||||
var isDelegate = false
|
||||
var firDelegateExpression: FirExpression? = null
|
||||
var isVar = false
|
||||
var receiverType: FirTypeRef? = null
|
||||
var returnType: FirTypeRef = implicitType
|
||||
@@ -655,7 +678,7 @@ class DeclarationsConverter(
|
||||
COLON -> isReturnType = true
|
||||
TYPE_REFERENCE -> if (isReturnType) returnType = convertType(it) else receiverType = convertType(it)
|
||||
TYPE_CONSTRAINT_LIST -> typeConstraints += convertTypeConstraints(it)
|
||||
PROPERTY_DELEGATE -> isDelegate = true
|
||||
PROPERTY_DELEGATE -> firDelegateExpression = expressionConverter.visitExpression(it)
|
||||
VAR_KEYWORD -> isVar = true
|
||||
PROPERTY_ACCESSOR -> {
|
||||
val propertyAccessor = convertGetterOrSetter(it, returnType)
|
||||
@@ -675,11 +698,7 @@ class DeclarationsConverter(
|
||||
returnType,
|
||||
isVar,
|
||||
firExpression,
|
||||
delegate = if (isDelegate) {
|
||||
FirExpressionStub(session, null)
|
||||
//TODO("not implemented")
|
||||
//{ property.delegate?.expression }.toFirExpression("Should have delegate")
|
||||
} else null
|
||||
delegate = firDelegateExpression
|
||||
)
|
||||
} else {
|
||||
FirMemberPropertyImpl(
|
||||
@@ -700,11 +719,7 @@ class DeclarationsConverter(
|
||||
firExpression,
|
||||
getter ?: FirDefaultPropertyGetter(session, null, returnType, modifiers.getVisibility()),
|
||||
if (isVar) setter ?: FirDefaultPropertySetter(session, null, returnType, modifiers.getVisibility()) else null,
|
||||
if (isDelegate) {
|
||||
FirExpressionStub(session, null)
|
||||
//TODO("not implemented")
|
||||
//{ property.delegate?.expression }.toFirExpression("Should have delegate")
|
||||
} else null
|
||||
firDelegateExpression
|
||||
).apply {
|
||||
this.typeParameters += firTypeParameters
|
||||
this.joinTypeParameters(typeConstraints)
|
||||
@@ -854,6 +869,7 @@ class DeclarationsConverter(
|
||||
|
||||
/**
|
||||
* @see org.jetbrains.kotlin.parsing.KotlinParsing.parseFunctionBody
|
||||
* @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.buildFirBody
|
||||
*/
|
||||
private fun visitFunctionBody(firBlock: FirBlock?, firExpression: FirExpression?): FirBlock? {
|
||||
return when {
|
||||
@@ -878,11 +894,10 @@ class DeclarationsConverter(
|
||||
/**
|
||||
* @see org.jetbrains.kotlin.parsing.KotlinParsing.parseBlock
|
||||
*/
|
||||
private fun visitBlock(block: LighterASTNode?): FirBlock {
|
||||
private fun visitBlock(block: LighterASTNode): FirBlock {
|
||||
return if (!stubMode) {
|
||||
//TODO("not implemented")
|
||||
FirBlockImpl(session, null)
|
||||
//visitStatements(ctx.statements())
|
||||
val blockTree = LightTree2Fir.buildLightTreeBlockExpression(block.getAsString())
|
||||
return DeclarationsConverter(session, stubMode, blockTree).convertBlockExpression(blockTree.root)
|
||||
} else {
|
||||
FirSingleExpressionBlock(
|
||||
session,
|
||||
@@ -1116,7 +1131,7 @@ class DeclarationsConverter(
|
||||
/**
|
||||
* @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 ->
|
||||
when (node.tokenType) {
|
||||
TYPE_PROJECTION -> container += convertTypeProjection(node)
|
||||
|
||||
+255
-5
@@ -8,39 +8,238 @@ package org.jetbrains.kotlin.fir.lightTree.converter
|
||||
import com.intellij.lang.LighterASTNode
|
||||
import com.intellij.util.diff.FlyweightCapableTreeStructure
|
||||
import org.jetbrains.kotlin.KtNodeTypes.*
|
||||
import org.jetbrains.kotlin.fir.FirReference
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.builder.parseCharacter
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
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.isExpression
|
||||
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.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.lexer.KtTokens.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.stubs.elements.KtConstantExpressionElementType
|
||||
import org.jetbrains.kotlin.resolve.constants.evaluate.*
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
class ExpressionsConverter(
|
||||
private val session: FirSession,
|
||||
val session: FirSession,
|
||||
private val stubMode: Boolean,
|
||||
tree: FlyweightCapableTreeStructure<LighterASTNode>
|
||||
tree: FlyweightCapableTreeStructure<LighterASTNode>,
|
||||
private val declarationsConverter: DeclarationsConverter
|
||||
) : BaseConverter(session, tree) {
|
||||
|
||||
/***** EXPRESSIONS *****/
|
||||
fun visitExpression(expression: LighterASTNode): FirExpression {
|
||||
if (!stubMode) {
|
||||
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)
|
||||
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)
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@@ -177,4 +410,21 @@ class ExpressionsConverter(
|
||||
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}")
|
||||
}
|
||||
}
|
||||
+161
@@ -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
|
||||
}
|
||||
+9
-3
@@ -7,11 +7,17 @@ package org.jetbrains.kotlin.fir.lightTree.compare
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
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.util.PathUtil
|
||||
import junit.framework.TestCase
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.FirRenderer
|
||||
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.walkTopDown
|
||||
import org.jetbrains.kotlin.parsing.KotlinParserDefinition
|
||||
@@ -23,7 +29,7 @@ import java.io.File
|
||||
@TestDataPath("\$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners::class)
|
||||
class TreesCompareTest : AbstractRawFirBuilderTestCase() {
|
||||
private fun compareBase(stubMode: Boolean, compareFir: (File) -> Boolean) {
|
||||
private fun compareBase(compareFir: (File) -> Boolean) {
|
||||
val path = System.getProperty("user.dir")
|
||||
var counter = 0
|
||||
var errorCounter = 0
|
||||
@@ -40,7 +46,7 @@ class TreesCompareTest : AbstractRawFirBuilderTestCase() {
|
||||
|
||||
private fun compareAll(stubMode: Boolean) {
|
||||
val lightTreeConverter = LightTree2Fir(stubMode, myProject)
|
||||
compareBase(stubMode) { file ->
|
||||
compareBase { file ->
|
||||
val text = FileUtil.loadFile(file, CharsetToolkit.UTF8, true).trim()
|
||||
|
||||
//light tree
|
||||
@@ -122,7 +128,7 @@ class TreesCompareTest : AbstractRawFirBuilderTestCase() {
|
||||
visitAssignment = visitAssignment
|
||||
)
|
||||
val lightTreeConverter = LightTree2Fir(stubMode, myProject)
|
||||
compareBase(stubMode) { file ->
|
||||
compareBase { file ->
|
||||
val text = FileUtil.loadFile(file, CharsetToolkit.UTF8, true).trim()
|
||||
|
||||
//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]
|
||||
}
|
||||
|
||||
internal fun IElementType.toUnaryName(): Name? {
|
||||
fun IElementType.toUnaryName(): Name? {
|
||||
return OperatorConventions.UNARY_OPERATION_NAMES[this]
|
||||
}
|
||||
|
||||
internal fun IElementType.toFirOperation(): FirOperation =
|
||||
fun IElementType.toFirOperation(): FirOperation =
|
||||
when (this) {
|
||||
KtTokens.LT -> FirOperation.LT
|
||||
KtTokens.GT -> FirOperation.GT
|
||||
@@ -181,8 +181,8 @@ internal fun IElementType.toFirOperation(): FirOperation =
|
||||
else -> throw AssertionError(this.toString())
|
||||
}
|
||||
|
||||
internal fun FirExpression.generateNotNullOrOther(
|
||||
session: FirSession, other: FirExpression, caseId: String, basePsi: KtElement
|
||||
fun FirExpression.generateNotNullOrOther(
|
||||
session: FirSession, other: FirExpression, caseId: String, basePsi: KtElement?
|
||||
): FirWhenExpression {
|
||||
val subjectName = Name.special("<$caseId>")
|
||||
val subjectVariable = generateTemporaryVariable(session, basePsi, subjectName, this)
|
||||
@@ -209,8 +209,8 @@ internal fun FirExpression.generateNotNullOrOther(
|
||||
}
|
||||
}
|
||||
|
||||
internal fun FirExpression.generateLazyLogicalOperation(
|
||||
other: FirExpression, isAnd: Boolean, basePsi: KtElement
|
||||
fun FirExpression.generateLazyLogicalOperation(
|
||||
other: FirExpression, isAnd: Boolean, basePsi: KtElement?
|
||||
): FirWhenExpression {
|
||||
val terminalExpression = FirConstExpressionImpl(psi, IrConstKind.Boolean, !isAnd)
|
||||
val terminalBlock = FirSingleExpressionBlock(terminalExpression)
|
||||
@@ -328,11 +328,11 @@ internal fun Array<KtStringTemplateEntry>.toInterpolatingCall(
|
||||
return if (hasExpressions) result!! else FirConstExpressionImpl(base, IrConstKind.String, sb.toString())
|
||||
}
|
||||
|
||||
internal fun FirExpression.generateContainsOperation(
|
||||
fun FirExpression.generateContainsOperation(
|
||||
argument: FirExpression,
|
||||
inverted: Boolean,
|
||||
base: KtExpression?,
|
||||
operationReference: KtOperationReferenceExpression
|
||||
operationReference: KtOperationReferenceExpression?
|
||||
): FirFunctionCall {
|
||||
val containsCall = FirFunctionCallImpl(base).apply {
|
||||
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 {
|
||||
calleeReference = FirSimpleNamedReference(psi, name)
|
||||
}
|
||||
|
||||
internal fun generateResolvedAccessExpression(psi: PsiElement?, variable: FirVariable<*>): FirQualifiedAccessExpression =
|
||||
fun generateResolvedAccessExpression(psi: PsiElement?, variable: FirVariable<*>): FirQualifiedAccessExpression =
|
||||
FirQualifiedAccessExpressionImpl(psi).apply {
|
||||
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
|
||||
): FirVariable<*> =
|
||||
FirVariableImpl(session, psi, name, FirImplicitTypeRefImpl(psi), false, initializer, FirVariableSymbol(name)).apply {
|
||||
|
||||
Reference in New Issue
Block a user