Move FirSourceElement to frontend.common (only move to save history)

This commit is contained in:
Ilya Chernikov
2021-09-17 11:31:55 +02:00
parent 8bd0b237b7
commit c50c8ad270
@@ -0,0 +1,414 @@
/*
* Copyright 2010-2021 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
import com.intellij.lang.LighterASTNode
import com.intellij.lang.TreeBackedLighterAST
import com.intellij.openapi.util.Ref
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.tree.IElementType
import com.intellij.util.diff.FlyweightCapableTreeStructure
sealed class FirSourceElementKind
object FirRealSourceElementKind : FirSourceElementKind()
sealed class FirFakeSourceElementKind : FirSourceElementKind() {
// for some fir expression implicit return typeRef is generated
// some of them are: break, continue, return, throw, string concat,
// destruction parameters, function literals, explicitly boolean expressions
object ImplicitTypeRef : FirFakeSourceElementKind()
// for each class special class self type ref is created
// and have a fake source referencing it
object ClassSelfTypeRef : FirFakeSourceElementKind()
// FirErrorTypeRef may be built using unresolved firExpression
// and have a fake source referencing it
object ErrorTypeRef : FirFakeSourceElementKind()
// for properties without accessors default getter & setter are generated
// they have a fake source which refers to property
object DefaultAccessor : FirFakeSourceElementKind()
// for delegated properties, getter & setter calls to the delegate
// they have a fake source which refers to the call that creates the delegate
object DelegatedPropertyAccessor : FirFakeSourceElementKind()
// for kt classes without implicit primary constructor one is generated
// with a fake source which refers to containing class
object ImplicitConstructor : FirFakeSourceElementKind()
// for constructors which do not have delegated constructor call the fake one is generated
// with a fake sources which refers to the original constructor
object DelegatingConstructorCall : FirFakeSourceElementKind()
// for enum entry with bodies the initializer in a form of anonymous object is generated
// with a fake sources which refers to the enum entry
object EnumInitializer : FirFakeSourceElementKind()
// for lambdas with implicit return the return statement is generated which is labeled
// with a fake sources which refers to the target expression
object GeneratedLambdaLabel : FirFakeSourceElementKind()
// for lambdas & functions with expression bodies the return statement is added
// with a fake sources which refers to the return target
object ImplicitReturn : FirFakeSourceElementKind()
// return expression in procedures -> return Unit
// with a fake sources which refers to the return statement
object ImplicitUnit : FirFakeSourceElementKind()
// delegates are wrapped into FirWrappedDelegateExpression
// with a fake sources which refers to delegated expression
object WrappedDelegate : FirFakeSourceElementKind()
// `for (i in list) { println(i) }` is converted to
// ```
// val <iterator>: = list.iterator()
// while(<iterator>.hasNext()) {
// val i = <iterator>.next()
// println(i)
// }
// ```
// where the generated WHILE loop has source element of initial FOR loop,
// other generated elements are marked as fake ones
object DesugaredForLoop : FirFakeSourceElementKind()
object ImplicitInvokeCall : FirFakeSourceElementKind()
// Consider an atomic qualified access like `i`. In the FIR tree, both the FirQualifiedAccessExpression and its calleeReference uses
// `i` as the source. Hence, this fake kind is set on the `calleeReference` to make sure no PSI element is shared by multiple FIR
// elements. This also applies to `this` and `super` references.
object ReferenceInAtomicQualifiedAccess : FirFakeSourceElementKind()
// for enum classes we have valueOf & values functions generated
// with a fake sources which refers to this the enum class
object EnumGeneratedDeclaration : FirFakeSourceElementKind()
// when (x) { "abc" -> 42 } --> when(val $subj = x) { $subj == "abc" -> 42 }
// where $subj == "42" has fake psi source which refers to "42" as inner expression
// and $subj fake source refers to "42" as KtWhenCondition
object WhenCondition : FirFakeSourceElementKind()
// for primary constructor parameter the corresponding class property is generated
// with a fake sources which refers to this the corresponding parameter
object PropertyFromParameter : FirFakeSourceElementKind()
// if (true) 1 --> if(true) { 1 }
// with a fake sources for the block which refers to the wrapped expression
object SingleExpressionBlock : FirFakeSourceElementKind()
// x++ -> x = x.inc()
// x = x++ -> x = { val <unary> = x; x = <unary>.inc(); <unary> }
object DesugaredIncrementOrDecrement : FirFakeSourceElementKind()
// x !in list --> !(x in list) where ! and !(x in list) will have a fake source
object DesugaredInvertedContains : FirFakeSourceElementKind()
// for data classes fir generates componentN() & copy() functions
// for componentN() functions the source will refer to the corresponding param and will be marked as a fake one
// for copy() functions the source will refer class to the param and will be marked as a fake one
object DataClassGeneratedMembers : FirFakeSourceElementKind()
// (vararg x: Int) --> (x: Array<out Int>) where array type ref has a fake source kind
object ArrayTypeFromVarargParameter : FirFakeSourceElementKind()
// val (a,b) = x --> val a = x.component1(); val b = x.component2()
// where componentN calls will have the fake source elements refer to the corresponding KtDestructuringDeclarationEntry
object DesugaredComponentFunctionCall : FirFakeSourceElementKind()
// when smart casts applied to the expression, its wrapped into FirExpressionWithSmartcast
// which type reference will have a fake source refer to a original source element of it
object SmartCastedTypeRef : FirFakeSourceElementKind()
// for safe call expressions like a?.foo() the FirSafeCallExpression is generated
// and it have a fake source
object DesugaredSafeCallExpression : FirFakeSourceElementKind()
// a += 2 --> a = a + 2
// where a + 2 will have a fake source
object DesugaredCompoundAssignment : FirFakeSourceElementKind()
// `a > b` will be wrapped in FirComparisonExpression
// with real source which points to initial `a > b` expression
// and inner FirFunctionCall will refer to a fake source
object GeneratedComparisonExpression : FirFakeSourceElementKind()
// a ?: b --> when(val $subj = a) { .... }
// where `val $subj = a` has a fake source
object WhenGeneratedSubject : FirFakeSourceElementKind()
// list[0] -> list.get(0) where name reference will have a fake source element
object ArrayAccessNameReference : FirFakeSourceElementKind()
// super.foo() --> super<Supertype>.foo()
// where `Supertype` has a fake source
object SuperCallImplicitType : FirFakeSourceElementKind()
// Consider `super<Supertype>.foo()`. The source PSI `Supertype` is referenced by both the qualified access expression
// `super<Supertype>` and the calleeExpression `super<Supertype>`. To avoid having two FIR elements sharing the same source, this fake
// source is assigned to the qualified access expression.
object SuperCallExplicitType : FirFakeSourceElementKind()
// fun foo(vararg args: Int) {}
// fun bar(1, 2, 3) --> [resolved] fun bar(VarargArgument(1, 2, 3))
object VarargArgument : FirFakeSourceElementKind()
// Part of desugared x?.y
object CheckedSafeCallSubject : FirFakeSourceElementKind()
// { it + 1} --> { it -> it + 1 }
// where `it` parameter declaration has fake source
object ItLambdaParameter : FirFakeSourceElementKind()
// for java annotations implicit constructor is generated
// with a fake source which refers to containing class
object ImplicitJavaAnnotationConstructor : FirFakeSourceElementKind()
// for java annotations constructor implicit parameters are generated
// with a fake source which refers to declared annotation methods
object ImplicitAnnotationAnnotationConstructorParameter : FirFakeSourceElementKind()
// for the implicit field storing the delegated object for class delegation
// with a fake source that refers to the KtExpression that creates the delegate
object ClassDelegationField : FirFakeSourceElementKind()
// for annotation moved to another element due to annotation use-site target
object FromUseSiteTarget : FirFakeSourceElementKind()
// for `@ParameterName` annotation call added to function types with names in the notation
// with a fake source that refers to the value parameter in the function type notation
// e.g., `(x: Int) -> Unit` becomes `Function1<@ParameterName("x") Int, Unit>`
object ParameterNameAnnotationCall : FirFakeSourceElementKind()
}
sealed class FirSourceElement {
abstract val elementType: IElementType
abstract val startOffset: Int
abstract val endOffset: Int
abstract val kind: FirSourceElementKind
abstract val lighterASTNode: LighterASTNode
abstract val treeStructure: FlyweightCapableTreeStructure<LighterASTNode>
/** Implementation must compute the hashcode from the source element. */
abstract override fun hashCode(): Int
/** Elements of the same source should be considered equal. */
abstract override fun equals(other: Any?): Boolean
}
// NB: in certain situations, psi.node could be null
// Potentially exceptions can be provoked by elementType / lighterASTNode
sealed class FirPsiSourceElement(val psi: PsiElement) : FirSourceElement() {
override val elementType: IElementType
get() = psi.node.elementType
override val startOffset: Int
get() = psi.textRange.startOffset
override val endOffset: Int
get() = psi.textRange.endOffset
override val lighterASTNode by lazy { TreeBackedLighterAST.wrap(psi.node) }
override val treeStructure: FlyweightCapableTreeStructure<LighterASTNode> by lazy { WrappedTreeStructure(psi.containingFile) }
internal class WrappedTreeStructure(file: PsiFile) : FlyweightCapableTreeStructure<LighterASTNode> {
private val lighterAST = TreeBackedLighterAST(file.node)
fun unwrap(node: LighterASTNode) = lighterAST.unwrap(node)
override fun toString(node: LighterASTNode): CharSequence = unwrap(node).text
override fun getRoot(): LighterASTNode = lighterAST.root
override fun getParent(node: LighterASTNode): LighterASTNode? =
unwrap(node).psi.parent?.node?.let { TreeBackedLighterAST.wrap(it) }
override fun getChildren(node: LighterASTNode, nodesRef: Ref<Array<LighterASTNode>>): Int {
val psi = unwrap(node).psi
val children = mutableListOf<PsiElement>()
var child = psi.firstChild
while (child != null) {
children += child
child = child.nextSibling
}
if (children.isEmpty()) {
nodesRef.set(LighterASTNode.EMPTY_ARRAY)
} else {
nodesRef.set(children.map { TreeBackedLighterAST.wrap(it.node) }.toTypedArray())
}
return children.size
}
override fun disposeChildren(p0: Array<out LighterASTNode>?, p1: Int) {
}
override fun getStartOffset(node: LighterASTNode): Int {
return getStartOffset(unwrap(node).psi)
}
private fun getStartOffset(element: PsiElement): Int {
var child = element.firstChild
if (child != null) {
while (child is PsiComment || child is PsiWhiteSpace) {
child = child.nextSibling
}
if (child != null) {
return getStartOffset(child)
}
}
return element.textRange.startOffset
}
override fun getEndOffset(node: LighterASTNode): Int {
return getEndOffset(unwrap(node).psi)
}
private fun getEndOffset(element: PsiElement): Int {
var child = element.lastChild
if (child != null) {
while (child is PsiComment || child is PsiWhiteSpace) {
child = child.prevSibling
}
if (child != null) {
return getEndOffset(child)
}
}
return element.textRange.endOffset
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as FirPsiSourceElement
if (psi != other.psi) return false
return true
}
override fun hashCode(): Int {
return psi.hashCode()
}
}
class FirRealPsiSourceElement(psi: PsiElement) : FirPsiSourceElement(psi) {
override val kind: FirSourceElementKind get() = FirRealSourceElementKind
}
class FirFakeSourceElement(psi: PsiElement, override val kind: FirFakeSourceElementKind) : FirPsiSourceElement(psi) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
if (!super.equals(other)) return false
other as FirFakeSourceElement
if (kind != other.kind) return false
return true
}
override fun hashCode(): Int {
var result = super.hashCode()
result = 31 * result + kind.hashCode()
return result
}
}
fun FirSourceElement.fakeElement(newKind: FirFakeSourceElementKind): FirSourceElement {
if (kind == newKind) return this
return when (this) {
is FirLightSourceElement -> FirLightSourceElement(lighterASTNode, startOffset, endOffset, treeStructure, newKind)
is FirPsiSourceElement -> FirFakeSourceElement(psi, newKind)
}
}
fun FirSourceElement.realElement(): FirSourceElement = when (this) {
is FirRealPsiSourceElement -> this
is FirLightSourceElement -> FirLightSourceElement(lighterASTNode, startOffset, endOffset, treeStructure, FirRealSourceElementKind)
is FirPsiSourceElement -> FirRealPsiSourceElement(psi)
}
class FirLightSourceElement(
override val lighterASTNode: LighterASTNode,
override val startOffset: Int,
override val endOffset: Int,
override val treeStructure: FlyweightCapableTreeStructure<LighterASTNode>,
override val kind: FirSourceElementKind = FirRealSourceElementKind,
) : FirSourceElement() {
override val elementType: IElementType
get() = lighterASTNode.tokenType
/**
* We can create a [FirLightSourceElement] from a [FirPsiSourceElement] by using [FirPsiSourceElement.lighterASTNode];
* [unwrapToFirPsiSourceElement] allows to get original [FirPsiSourceElement] in such case.
*
* If it is `pure` [FirLightSourceElement], i.e, compiler created it in light tree mode, then return [unwrapToFirPsiSourceElement] `null`.
* Otherwise, return some not-null result.
*/
fun unwrapToFirPsiSourceElement(): FirPsiSourceElement? {
if (treeStructure !is FirPsiSourceElement.WrappedTreeStructure) return null
val node = treeStructure.unwrap(lighterASTNode)
return node.psi?.toFirPsiSourceElement(kind)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as FirLightSourceElement
if (lighterASTNode != other.lighterASTNode) return false
if (startOffset != other.startOffset) return false
if (endOffset != other.endOffset) return false
if (treeStructure != other.treeStructure) return false
if (kind != other.kind) return false
return true
}
override fun hashCode(): Int {
var result = lighterASTNode.hashCode()
result = 31 * result + startOffset
result = 31 * result + endOffset
result = 31 * result + treeStructure.hashCode()
result = 31 * result + kind.hashCode()
return result
}
}
val FirSourceElement?.psi: PsiElement? get() = (this as? FirPsiSourceElement)?.psi
val FirSourceElement?.text: CharSequence?
get() = when (this) {
is FirPsiSourceElement -> psi.text
is FirLightSourceElement -> treeStructure.toString(lighterASTNode)
else -> null
}
@Suppress("NOTHING_TO_INLINE")
inline fun PsiElement.toFirPsiSourceElement(kind: FirSourceElementKind = FirRealSourceElementKind): FirPsiSourceElement = when (kind) {
is FirRealSourceElementKind -> FirRealPsiSourceElement(this)
is FirFakeSourceElementKind -> FirFakeSourceElement(this, kind)
}
@Suppress("NOTHING_TO_INLINE")
inline fun LighterASTNode.toFirLightSourceElement(
tree: FlyweightCapableTreeStructure<LighterASTNode>,
kind: FirSourceElementKind = FirRealSourceElementKind,
startOffset: Int = this.startOffset,
endOffset: Int = this.endOffset
): FirLightSourceElement = FirLightSourceElement(this, startOffset, endOffset, tree, kind)