Write annotation's conversion (light tree to FIR)

This commit is contained in:
Ivan Cilcic
2019-07-09 16:56:07 +03:00
committed by Mikhail Glukhikh
parent 8bcea1cf85
commit 5a24c2253a
5 changed files with 221 additions and 74 deletions
@@ -11,7 +11,9 @@ import com.intellij.psi.tree.IElementType
import com.intellij.util.diff.FlyweightCapableTreeStructure
import org.jetbrains.kotlin.fir.FirSession
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.*
open class BaseConverter(
session: FirSession,
@@ -39,12 +41,12 @@ open class BaseConverter(
return kidsRef.get()
}
protected inline fun LighterASTNode.forEachChildren(f: (LighterASTNode) -> Unit) {
protected inline fun LighterASTNode.forEachChildren(vararg skipTokens: KtToken, f: (LighterASTNode) -> Unit) {
val kidsArray = this.getChildrenAsArray()
for (kid in kidsArray) {
if (kid == null) continue
if (kid == null) break
val tokenType = kid.tokenType
if (KtTokens.COMMENTS.contains(tokenType) || tokenType == KtTokens.WHITE_SPACE || tokenType == KtTokens.SEMICOLON) continue
if (COMMENTS.contains(tokenType) || tokenType == WHITE_SPACE || tokenType == SEMICOLON || tokenType in skipTokens) continue
f(kid)
}
}
@@ -54,9 +56,9 @@ open class BaseConverter(
val container = mutableListOf<T>()
for (kid in kidsArray) {
if (kid == null) continue
if (kid == null) break
val tokenType = kid.tokenType
if (KtTokens.COMMENTS.contains(tokenType) || tokenType == KtTokens.WHITE_SPACE || tokenType == KtTokens.SEMICOLON) continue
if (COMMENTS.contains(tokenType) || tokenType == WHITE_SPACE || tokenType == SEMICOLON) continue
f(kid, container)
}
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.fir.FirFunctionTarget
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.impl.*
@@ -56,6 +57,10 @@ object ConverterUtil {
return this.toString()
}
fun LighterASTNode.getAsStringUnescapedValue(): String {
return this.toString().replaceFirst("\\", "")
}
fun LighterASTNode.isExpression(): Boolean {
return when (this.tokenType) {
is KtNodeType,
@@ -139,8 +144,7 @@ object ConverterUtil {
}
fun <T : FirCallWithArgumentList> T.extractArgumentsFrom(container: List<FirExpression>, stubMode: Boolean): T {
if (!stubMode) {
//TODO("not implemented")
if (!stubMode || this is FirAnnotationCall) {
this.arguments += container
}
return this
@@ -165,7 +169,8 @@ object ClassNameUtil {
val currentClassId
get() = ClassId(
packageFqName,
className, false)
className, false
)
fun callableIdForName(name: Name, local: Boolean = false) =
when {
@@ -173,14 +178,16 @@ object ClassNameUtil {
className == FqName.ROOT -> CallableId(packageFqName, name)
else -> CallableId(
packageFqName,
className, name)
className, name
)
}
fun callableIdForClassConstructor() =
if (className == FqName.ROOT) CallableId(packageFqName, Name.special("<anonymous-init>"))
else CallableId(
packageFqName,
className, className.shortName())
className, className.shortName()
)
var className: FqName = FqName.ROOT
}
@@ -257,11 +264,13 @@ object DataClassUtil {
firPrimaryConstructor: FirConstructor,
properties: List<FirProperty>
) {
val symbol = FirFunctionSymbol(CallableId(
ClassNameUtil.packageFqName,
ClassNameUtil.className,
copyName
))
val symbol = FirFunctionSymbol(
CallableId(
ClassNameUtil.packageFqName,
ClassNameUtil.className,
copyName
)
)
firClass.addDeclaration(
FirMemberFunctionImpl(
session, null, symbol, copyName,
@@ -53,6 +53,7 @@ class DeclarationsConverter(
private val stubMode: Boolean,
tree: FlyweightCapableTreeStructure<LighterASTNode>
) : BaseConverter(session, tree) {
private val expressionConverter = ExpressionsConverter(session, stubMode, tree)
/**
* [org.jetbrains.kotlin.parsing.KotlinParsing.parseFile]
@@ -171,7 +172,7 @@ class DeclarationsConverter(
modifiers.forEachChildren {
when (it.tokenType) {
ANNOTATION -> modifier.annotations += convertAnnotation(it)
ANNOTATION_ENTRY -> modifier.annotations += convertAnnotationEntry(it, null)
ANNOTATION_ENTRY -> modifier.annotations += convertAnnotationEntry(it)
is KtModifierKeywordToken -> modifier.addModifier(it)
}
}
@@ -186,7 +187,7 @@ class DeclarationsConverter(
modifiers.forEachChildren {
when (it.tokenType) {
ANNOTATION -> typeModifierList.annotations += convertAnnotation(it)
ANNOTATION_ENTRY -> typeModifierList.annotations += convertAnnotationEntry(it, null)
ANNOTATION_ENTRY -> typeModifierList.annotations += convertAnnotationEntry(it)
is KtModifierKeywordToken -> typeModifierList.addModifier(it)
}
}
@@ -201,7 +202,7 @@ class DeclarationsConverter(
modifiers.forEachChildren {
when (it.tokenType) {
ANNOTATION -> typeArgumentModifierList.annotations += convertAnnotation(it)
ANNOTATION_ENTRY -> typeArgumentModifierList.annotations += convertAnnotationEntry(it, null)
ANNOTATION_ENTRY -> typeArgumentModifierList.annotations += convertAnnotationEntry(it)
is KtModifierKeywordToken -> typeArgumentModifierList.addModifier(it)
}
}
@@ -216,7 +217,7 @@ class DeclarationsConverter(
modifiers.forEachChildren {
when (it.tokenType) {
ANNOTATION -> modifier.annotations += convertAnnotation(it)
ANNOTATION_ENTRY -> modifier.annotations += convertAnnotationEntry(it, null)
ANNOTATION_ENTRY -> modifier.annotations += convertAnnotationEntry(it)
is KtModifierKeywordToken -> modifier.addModifier(it)
}
}
@@ -231,7 +232,7 @@ class DeclarationsConverter(
return fileAnnotationList.forEachChildrenReturnList { node, container ->
when (node.tokenType) {
ANNOTATION -> container += convertAnnotation(node)
ANNOTATION_ENTRY -> container += convertAnnotationEntry(node, AnnotationUseSiteTarget.FILE)
ANNOTATION_ENTRY -> container += convertAnnotationEntry(node)
}
}
}
@@ -250,12 +251,13 @@ class DeclarationsConverter(
}
/**
* org.jetbrains.kotlin.parsing.KotlinParsing.parseAnnotationTarget
* @see org.jetbrains.kotlin.parsing.KotlinParsing.parseAnnotationTarget
*/
private fun convertAnnotationTarget(annotationUseSiteTarget: LighterASTNode): AnnotationUseSiteTarget {
lateinit var annotationTarget: AnnotationUseSiteTarget
annotationUseSiteTarget.forEachChildren {
when (it.tokenType) {
FIELD_KEYWORD -> annotationTarget = AnnotationUseSiteTarget.FIELD
FILE_KEYWORD -> annotationTarget = AnnotationUseSiteTarget.FILE
PROPERTY_KEYWORD -> annotationTarget = AnnotationUseSiteTarget.PROPERTY
GET_KEYWORD -> annotationTarget = AnnotationUseSiteTarget.PROPERTY_GETTER
@@ -276,18 +278,22 @@ class DeclarationsConverter(
*/
private fun convertAnnotationEntry(
unescapedAnnotation: LighterASTNode,
annotationUseSiteTarget: AnnotationUseSiteTarget?
defaultAnnotationUseSiteTarget: AnnotationUseSiteTarget? = null
): FirAnnotationCall {
//TODO not implemented
val pair = convertConstructorInvocation(unescapedAnnotation)
var annotationUseSiteTarget: AnnotationUseSiteTarget? = null
lateinit var constructorCalleePair: Pair<FirTypeRef, List<FirExpression>>
unescapedAnnotation.forEachChildren {
when (it.tokenType) {
ANNOTATION_TARGET -> annotationUseSiteTarget = convertAnnotationTarget(it)
CONSTRUCTOR_CALLEE -> constructorCalleePair = convertConstructorInvocation(unescapedAnnotation)
}
}
return FirAnnotationCallImpl(
session,
null,
annotationUseSiteTarget,
pair.first
).apply {
arguments += pair.second
}
annotationUseSiteTarget ?: defaultAnnotationUseSiteTarget,
constructorCalleePair.first
).extractArgumentsFrom(constructorCalleePair.second, stubMode)
}
/***** DECLARATIONS *****/
@@ -566,10 +572,11 @@ class DeclarationsConverter(
classWrapper: ClassWrapper
): FirDelegatedConstructorCallImpl {
var thisKeywordPresent = false
val firValueArguments = mutableListOf<FirExpression>()
constructorDelegationCall.forEachChildren {
when (it.tokenType) {
CONSTRUCTOR_DELEGATION_REFERENCE -> if (it.getAsString() == "this") thisKeywordPresent = true
VALUE_ARGUMENT_LIST -> "" //TODO implement
VALUE_ARGUMENT_LIST -> firValueArguments += expressionConverter.convertValueArguments(it)
}
}
@@ -585,7 +592,7 @@ class DeclarationsConverter(
null,
delegatedType,
isThis
).extractArgumentsFrom(listOf(), stubMode) //TODO implement
).extractArgumentsFrom(firValueArguments, stubMode)
}
/**
@@ -654,7 +661,7 @@ class DeclarationsConverter(
val propertyAccessor = convertGetterOrSetter(it, returnType)
if (propertyAccessor.isGetter) getter = propertyAccessor else setter = propertyAccessor
}
else -> if (it.isExpression()) firExpression = visitExpression(it)
else -> if (it.isExpression()) firExpression = expressionConverter.visitExpression(it)
}
}
@@ -724,7 +731,7 @@ class DeclarationsConverter(
TYPE_REFERENCE -> returnType = convertType(it)
VALUE_PARAMETER_LIST -> firValueParameters = convertSetterParameter(it, propertyTypeRef)
BLOCK -> firBlock = visitBlock(it)
else -> if (it.isExpression()) firExpression = visitExpression(it)
else -> if (it.isExpression()) firExpression = expressionConverter.visitExpression(it)
}
}
@@ -801,7 +808,7 @@ class DeclarationsConverter(
TYPE_REFERENCE -> if (isReturnType) returnType = convertType(it) else receiverType = convertType(it)
TYPE_CONSTRAINT_LIST -> typeConstraints += convertTypeConstraints(it)
BLOCK -> firBlock = visitBlock(it)
else -> if (it.isExpression()) firExpression = visitExpression(it)
else -> if (it.isExpression()) firExpression = expressionConverter.visitExpression(it)
}
}
@@ -923,7 +930,7 @@ class DeclarationsConverter(
constructorInvocation.forEachChildren {
when (it.tokenType) {
CONSTRUCTOR_CALLEE -> firTypeRef = convertType(it)
VALUE_ARGUMENT_LIST -> firValueArguments += convertValueArguments(it)
VALUE_ARGUMENT_LIST -> firValueArguments += expressionConverter.convertValueArguments(it)
}
}
return Pair(firTypeRef, firValueArguments)
@@ -942,7 +949,7 @@ class DeclarationsConverter(
explicitDelegation.forEachChildren {
when (it.tokenType) {
TYPE_REFERENCE -> firTypeRef = convertType(it)
else -> if (it.isExpression()) firExpression = visitExpression(it)
else -> if (it.isExpression()) firExpression = expressionConverter.visitExpression(it)
}
}
@@ -1193,7 +1200,7 @@ class DeclarationsConverter(
VAR_KEYWORD -> isVar = true
IDENTIFIER -> identifier = it.getAsString()
TYPE_REFERENCE -> firType = convertType(it)
else -> if (it.isExpression()) firExpression = visitExpression(it)
else -> if (it.isExpression()) firExpression = expressionConverter.visitExpression(it)
}
}
@@ -1209,41 +1216,4 @@ class DeclarationsConverter(
).apply { annotations += modifiers.annotations }
return ValueParameter(isVal, isVar, modifiers, firValueParameter)
}
/***** EXPRESSIONS *****/
//TODO move to parsing class?
/**
* @see org.jetbrains.kotlin.parsing.KotlinExpressionParsing.parseValueArgumentList
*/
private fun convertValueArguments(valueArguments: LighterASTNode): List<FirExpression> {
return valueArguments.forEachChildrenReturnList { node, container ->
when (node.tokenType) {
VALUE_ARGUMENT -> container += convertValueArgument(node)
}
}
}
private fun convertValueArgument(valueArgument: LighterASTNode): FirExpression {
return FirErrorExpressionImpl(session, null, "Not implemented")
//TODO implement
/*
this ?: return FirErrorExpressionImpl(session, this as? KtElement, "No argument given")
val expression = this.getArgumentExpression()
return when (expression) {
is KtConstantExpression, is KtStringTemplateExpression -> {
expression.accept(this@Visitor, Unit) as FirExpression
}
else -> {
{ expression }.toFirExpression("Argument is absent")
}
}
*/
}
private fun visitExpression(expression: LighterASTNode): FirExpression {
return if (stubMode) FirExpressionStub(session, null)
else TODO("not implemented")
}
}
@@ -7,7 +7,19 @@ 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.FirSession
import org.jetbrains.kotlin.fir.builder.parseCharacter
import org.jetbrains.kotlin.fir.expressions.FirExpression
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.ir.expressions.IrConstKind
import org.jetbrains.kotlin.lexer.KtTokens.*
import org.jetbrains.kotlin.psi.stubs.elements.KtConstantExpressionElementType
import org.jetbrains.kotlin.resolve.constants.evaluate.*
class ExpressionsConverter(
private val session: FirSession,
@@ -15,4 +27,154 @@ class ExpressionsConverter(
tree: FlyweightCapableTreeStructure<LighterASTNode>
) : BaseConverter(session, tree) {
/***** EXPRESSIONS *****/
fun visitExpression(expression: LighterASTNode): FirExpression {
if (!stubMode) {
when (expression.tokenType) {
STRING_TEMPLATE -> return convertStringTemplate(expression)
is KtConstantExpressionElementType -> return convertConstantExpression(expression)
}
TODO("not implemented")
}
return FirExpressionStub(session, null)
}
/**
* @see org.jetbrains.kotlin.fir.builder.ConversionUtilsKt.toInterpolatingCall
*/
private fun convertStringTemplate(stringTemplate: LighterASTNode): FirExpression {
val sb = StringBuilder()
var hasExpressions = false
var result: FirExpression? = null
var callCreated = false
stringTemplate.forEachChildren(OPEN_QUOTE, CLOSING_QUOTE) {
val nextArgument = when (it.tokenType) {
LITERAL_STRING_TEMPLATE_ENTRY -> {
sb.append(it.getAsString())
FirConstExpressionImpl(session, null, IrConstKind.String, it.getAsString())
}
ESCAPE_STRING_TEMPLATE_ENTRY -> {
sb.append(it.getAsStringUnescapedValue())
FirConstExpressionImpl(session, null, IrConstKind.String, it.getAsStringUnescapedValue())
}
SHORT_STRING_TEMPLATE_ENTRY, LONG_STRING_TEMPLATE_ENTRY -> {
hasExpressions = true
convertShortOrLongStringTemplate(it)
}
else -> {
hasExpressions = true
FirErrorExpressionImpl(session, null, "Incorrect template entry: ${it.getAsString()}")
}
}
result = when {
result == null -> nextArgument
callCreated && result is FirStringConcatenationCallImpl -> (result as FirStringConcatenationCallImpl).apply {
//TODO smart cast to FirStringConcatenationCallImpl isn't working
arguments += nextArgument
}
else -> {
callCreated = true
FirStringConcatenationCallImpl(session, null).apply {
arguments += result!!
arguments += nextArgument
}
}
}
}
return if (hasExpressions) result!! else FirConstExpressionImpl(session, null, IrConstKind.String, sb.toString())
}
private fun convertShortOrLongStringTemplate(shortOrLongString: LighterASTNode): FirExpression {
lateinit var firExpression: FirExpression
shortOrLongString.forEachChildren {
firExpression = visitExpression(it)
}
return firExpression
}
/**
* @see org.jetbrains.kotlin.fir.builder.ConversionUtilsKt.generateConstantExpressionByLiteral
*/
private fun convertConstantExpression(constantExpression: LighterASTNode): FirExpression {
val type = constantExpression.tokenType
val text: String = constantExpression.getAsString()
val convertedText: Any? = when (type) {
INTEGER_CONSTANT, FLOAT_CONSTANT -> parseNumericLiteral(text, type)
BOOLEAN_CONSTANT -> parseBoolean(text)
else -> null
}
return when (type) {
INTEGER_CONSTANT ->
if (convertedText is Long &&
(hasLongSuffix(text) || hasUnsignedLongSuffix(text) || hasUnsignedSuffix(text) ||
convertedText > Int.MAX_VALUE || convertedText < Int.MIN_VALUE)
) {
FirConstExpressionImpl(
session, null, IrConstKind.Long, convertedText, "Incorrect long: $text"
)
} else if (convertedText is Number) {
// TODO: support byte / short
FirConstExpressionImpl(session, null, IrConstKind.Int, convertedText.toInt(), "Incorrect int: $text")
} else {
FirErrorExpressionImpl(session, null, reason = "Incorrect constant expression: $text")
}
FLOAT_CONSTANT ->
if (convertedText is Float) {
FirConstExpressionImpl(
session, null, IrConstKind.Float, convertedText, "Incorrect float: $text"
)
} else {
FirConstExpressionImpl(
session, null, IrConstKind.Double, convertedText as Double, "Incorrect double: $text"
)
}
CHARACTER_CONSTANT ->
FirConstExpressionImpl(
session, null, IrConstKind.Char, text.parseCharacter(), "Incorrect character: $text"
)
BOOLEAN_CONSTANT ->
FirConstExpressionImpl(session, null, IrConstKind.Boolean, convertedText as Boolean)
NULL ->
FirConstExpressionImpl(session, null, IrConstKind.Null, null)
else ->
throw AssertionError("Unknown literal type: $type, $text")
}
}
/**
* @see org.jetbrains.kotlin.parsing.KotlinExpressionParsing.parseValueArgumentList
*/
fun convertValueArguments(valueArguments: LighterASTNode): List<FirExpression> {
return valueArguments.forEachChildrenReturnList { node, container ->
when (node.tokenType) {
VALUE_ARGUMENT -> container += convertValueArgument(node)
}
}
}
/**
* @see org.jetbrains.kotlin.parsing.KotlinExpressionParsing.parseValueArgument
* @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.toFirExpression(org.jetbrains.kotlin.psi.ValueArgument)
*/
private fun convertValueArgument(valueArgument: LighterASTNode): FirExpression {
var identifier: String? = null
var isSpread = false
lateinit var firExpression: FirExpression
valueArgument.forEachChildren {
when (it.tokenType) {
VALUE_ARGUMENT_NAME -> identifier = it.getAsString()
MUL -> isSpread = true
STRING_TEMPLATE -> firExpression = convertStringTemplate(it)
is KtConstantExpressionElementType -> firExpression = convertConstantExpression(it)
else -> if (it.isExpression()) firExpression = visitExpression(it)
}
}
return when {
identifier != null -> FirNamedArgumentExpressionImpl(session, null, identifier.nameAsSafeName(), isSpread, firExpression)
isSpread -> FirSpreadArgumentExpressionImpl(session, null, firExpression)
else -> firExpression
}
}
}
@@ -64,6 +64,10 @@ class TreesCompareTest : AbstractRawFirBuilderTestCase() {
compare(stubMode = true)
}
fun testCompareAll() {
compare(stubMode = false)
}
fun testStubCompareWithoutAnnotations() {
compare(stubMode = true, visitAnnotation = false)
}