FIR IDE: introduce KtBackingFieldSymbol
This commit is contained in:
+21
-17
@@ -15,6 +15,8 @@ import com.intellij.psi.util.parentOfType
|
||||
import org.jetbrains.kotlin.idea.KotlinIdeaAnalysisBundle
|
||||
import org.jetbrains.kotlin.idea.fir.highlighter.textAttributesKeyForPropertyDeclaration
|
||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtBackingFieldSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtSymbol
|
||||
import org.jetbrains.kotlin.idea.highlighter.NameHighlighter
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
@@ -40,21 +42,29 @@ internal class VariableReferenceHighlightingVisitor(
|
||||
return
|
||||
}
|
||||
|
||||
val target = expression.mainReference.resolve()
|
||||
|
||||
when {
|
||||
target != null && expression.isBackingField(target) -> Colors.BACKING_FIELD_VARIABLE
|
||||
target is PsiMethod -> Colors.SYNTHETIC_EXTENSION_PROPERTY
|
||||
target != null -> textAttributesKeyForPropertyDeclaration(target)
|
||||
else -> null
|
||||
}?.let { attribute ->
|
||||
highlightName(expression, attribute)
|
||||
if (target?.isMutableVariable() == true) {
|
||||
highlightName(expression, Colors.MUTABLE_VARIABLE)
|
||||
with(analysisSession) {
|
||||
val targetSymbol = expression.mainReference.resolveToSymbol()
|
||||
val target = expression.mainReference.resolve()
|
||||
when {
|
||||
targetSymbol is KtBackingFieldSymbol -> Colors.BACKING_FIELD_VARIABLE
|
||||
target is PsiMethod -> Colors.SYNTHETIC_EXTENSION_PROPERTY
|
||||
target != null -> textAttributesKeyForPropertyDeclaration(target)
|
||||
else -> null
|
||||
}?.let { attribute ->
|
||||
highlightName(expression, attribute)
|
||||
if (target?.isMutableVariable() == true || targetSymbol != null && isBackingFieldReferencingMutableVariable(targetSymbol)) {
|
||||
highlightName(expression, Colors.MUTABLE_VARIABLE)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
private fun KtAnalysisSession.isBackingFieldReferencingMutableVariable(symbol: KtSymbol): Boolean {
|
||||
if (symbol !is KtBackingFieldSymbol) return false
|
||||
return !symbol.owningProperty.isVal
|
||||
}
|
||||
|
||||
private fun KtSimpleNameExpression.isByNameArgumentReference() =
|
||||
parent is KtValueArgumentName
|
||||
|
||||
@@ -75,9 +85,3 @@ private fun KtSimpleNameExpression.isAssignmentReference(): Boolean {
|
||||
return operationSignTokenType == KtTokens.EQ
|
||||
}
|
||||
|
||||
private fun KtSimpleNameExpression.isBackingField(target: PsiElement): Boolean {
|
||||
if (getReferencedName() != "field") return false
|
||||
if (target !is KtProperty) return false
|
||||
val accessor = parentOfType<KtPropertyAccessor>() ?: return false
|
||||
return accessor.parent == target
|
||||
}
|
||||
|
||||
@@ -93,4 +93,12 @@ enum class KtSymbolOrigin {
|
||||
|
||||
|
||||
JAVA_SYNTHETIC_PROPERTY,
|
||||
|
||||
/**
|
||||
* Declaration is backing field of some member property
|
||||
* A symbol kind of [KtBackingFieldSymbol]
|
||||
*
|
||||
* @see KtBackingFieldSymbol
|
||||
*/
|
||||
PROPERTY_BACKING_FIELD,
|
||||
}
|
||||
+32
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.frontend.api.symbols
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.*
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.pointers.KtSymbolPointer
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
@@ -15,6 +16,33 @@ sealed class KtVariableLikeSymbol : KtCallableSymbol(), KtNamedSymbol, KtSymbolW
|
||||
abstract override fun createPointer(): KtSymbolPointer<KtVariableLikeSymbol>
|
||||
}
|
||||
|
||||
/**
|
||||
* Backing field of some member property
|
||||
*
|
||||
* E.g,
|
||||
* ```
|
||||
* val x: Int = 10
|
||||
* get() = field<caret>
|
||||
* ```
|
||||
*
|
||||
* Symbol at caret will be resolved to a [KtBackingFieldSymbol]
|
||||
*/
|
||||
abstract class KtBackingFieldSymbol : KtVariableLikeSymbol() {
|
||||
abstract val owningProperty: KtKotlinPropertySymbol
|
||||
|
||||
final override val name: Name get() = fieldName
|
||||
final override val psi: PsiElement? get() = null
|
||||
final override val symbolKind: KtSymbolKind get() = KtSymbolKind.LOCAL
|
||||
final override val origin: KtSymbolOrigin get() = KtSymbolOrigin.PROPERTY_BACKING_FIELD
|
||||
|
||||
abstract override fun createPointer(): KtSymbolPointer<KtVariableLikeSymbol>
|
||||
|
||||
companion object {
|
||||
private val fieldName = Name.identifier("field")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
abstract class KtEnumEntrySymbol : KtVariableLikeSymbol(), KtSymbolWithMembers, KtSymbolWithKind {
|
||||
final override val symbolKind: KtSymbolKind get() = KtSymbolKind.MEMBER
|
||||
abstract val containingEnumClassIdIfNonLocal: ClassId?
|
||||
@@ -69,6 +97,8 @@ abstract class KtKotlinPropertySymbol : KtPropertySymbol() {
|
||||
abstract val isLateInit: Boolean
|
||||
|
||||
abstract val isConst: Boolean
|
||||
|
||||
abstract override fun createPointer(): KtSymbolPointer<KtKotlinPropertySymbol>
|
||||
}
|
||||
|
||||
abstract class KtSyntheticJavaPropertySymbol : KtPropertySymbol() {
|
||||
@@ -80,6 +110,8 @@ abstract class KtSyntheticJavaPropertySymbol : KtPropertySymbol() {
|
||||
|
||||
abstract val javaGetterName: Name
|
||||
abstract val javaSetterName: Name?
|
||||
|
||||
abstract override fun createPointer(): KtSymbolPointer<KtSyntheticJavaPropertySymbol>
|
||||
}
|
||||
|
||||
abstract class KtLocalVariableSymbol : KtVariableSymbol(), KtSymbolWithKind {
|
||||
|
||||
+15
-2
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.fir.resolve.getSymbolByLookupTag
|
||||
import org.jetbrains.kotlin.fir.resolve.inference.isFunctionalType
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirBackingFieldSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassifierSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
@@ -46,7 +47,8 @@ internal class KtSymbolByFirBuilder private constructor(
|
||||
val withReadOnlyCaching: Boolean,
|
||||
private val symbolsCache: BuilderCache<FirDeclaration, KtSymbol>,
|
||||
private val filesCache: BuilderCache<FirFile, KtFileSymbol>,
|
||||
private val typesCache: BuilderCache<ConeKotlinType, KtType>
|
||||
private val backingFieldCache: BuilderCache<FirBackingFieldSymbol, KtBackingFieldSymbol>,
|
||||
private val typesCache: BuilderCache<ConeKotlinType, KtType>,
|
||||
) : ValidityTokenOwner {
|
||||
private val resolveState by weakRef(resolveState)
|
||||
|
||||
@@ -70,6 +72,7 @@ internal class KtSymbolByFirBuilder private constructor(
|
||||
withReadOnlyCaching = false,
|
||||
symbolsCache = BuilderCache(),
|
||||
typesCache = BuilderCache(),
|
||||
backingFieldCache = BuilderCache(),
|
||||
filesCache = BuilderCache(),
|
||||
)
|
||||
|
||||
@@ -84,6 +87,7 @@ internal class KtSymbolByFirBuilder private constructor(
|
||||
symbolsCache = symbolsCache.createReadOnlyCopy(),
|
||||
typesCache = typesCache.createReadOnlyCopy(),
|
||||
filesCache = filesCache.createReadOnlyCopy(),
|
||||
backingFieldCache = backingFieldCache.createReadOnlyCopy(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -215,7 +219,7 @@ internal class KtSymbolByFirBuilder private constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun buildPropertySymbol(fir: FirProperty): KtPropertySymbol {
|
||||
fun buildPropertySymbol(fir: FirProperty): KtKotlinPropertySymbol {
|
||||
require(!fir.isLocal)
|
||||
require(fir !is FirSyntheticProperty)
|
||||
return symbolsCache.cache(fir) {
|
||||
@@ -262,6 +266,15 @@ internal class KtSymbolByFirBuilder private constructor(
|
||||
return symbolsCache.cache(fir) { KtFirJavaFieldSymbol(fir, resolveState, token, this@KtSymbolByFirBuilder) }
|
||||
}
|
||||
|
||||
fun buildBackingFieldSymbol(fir: FirBackingFieldSymbol): KtFirBackingFieldSymbol {
|
||||
return backingFieldCache.cache(fir) { KtFirBackingFieldSymbol(fir.fir, resolveState, token, this@KtSymbolByFirBuilder) }
|
||||
}
|
||||
|
||||
fun buildBackingFieldSymbolByProperty(fir: FirProperty): KtFirBackingFieldSymbol {
|
||||
val backingFieldSymbol = fir.backingFieldSymbol
|
||||
return buildBackingFieldSymbol(backingFieldSymbol)
|
||||
}
|
||||
|
||||
private fun FirField.isJavaFieldOrSubstitutionOverrideOfJavaField(): Boolean = when (this) {
|
||||
is FirJavaField -> true
|
||||
is FirFieldImpl -> (this as FirField).originalForSubstitutionOverride?.isJavaFieldOrSubstitutionOverrideOfJavaField() == true
|
||||
|
||||
+6
@@ -32,12 +32,18 @@ internal class KtFirSymbolContainingDeclarationProvider(
|
||||
getContainingDeclarationForKotlinInSourceSymbol(symbol)
|
||||
KtSymbolOrigin.LIBRARY, KtSymbolOrigin.JAVA, KtSymbolOrigin.JAVA_SYNTHETIC_PROPERTY ->
|
||||
getContainingDeclarationForLibrarySymbol(symbol)
|
||||
KtSymbolOrigin.PROPERTY_BACKING_FIELD -> getContainingDeclarationForBackingFieldSymbol(symbol)
|
||||
KtSymbolOrigin.INTERSECTION_OVERRIDE -> TODO()
|
||||
KtSymbolOrigin.SAM_CONSTRUCTOR -> TODO()
|
||||
KtSymbolOrigin.DELEGATED -> TODO()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getContainingDeclarationForBackingFieldSymbol(symbol: KtSymbolWithKind): KtSymbolWithKind {
|
||||
require(symbol is KtBackingFieldSymbol)
|
||||
return symbol.owningProperty
|
||||
}
|
||||
|
||||
private fun getContainingDeclarationForKotlinInSourceSymbol(symbol: KtSymbolWithKind): KtSymbolWithKind = with(analysisSession) {
|
||||
require(symbol.origin == KtSymbolOrigin.SOURCE || symbol.origin == KtSymbolOrigin.SOURCE_MEMBER_GENERATED)
|
||||
val psi = symbol.getPsi()
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.idea.frontend.api.fir.symbols
|
||||
|
||||
import org.jetbrains.kotlin.fir.declarations.FirProperty
|
||||
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||
import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState
|
||||
import org.jetbrains.kotlin.idea.frontend.api.ValidityToken
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.KtSymbolByFirBuilder
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.pointers.KtFirBackingFieldSymbolPointer
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.utils.cached
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.utils.firRef
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.utils.weakRef
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtBackingFieldSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtKotlinPropertySymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtTypeAndAnnotations
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.pointers.KtSymbolPointer
|
||||
|
||||
internal class KtFirBackingFieldSymbol(
|
||||
propertyFir: FirProperty,
|
||||
resolveState: FirModuleResolveState,
|
||||
override val token: ValidityToken,
|
||||
_builder: KtSymbolByFirBuilder
|
||||
) : KtBackingFieldSymbol(){
|
||||
private val builder by weakRef(_builder)
|
||||
private val propertyFirRef = firRef(propertyFir, resolveState)
|
||||
|
||||
override val annotatedType: KtTypeAndAnnotations by cached {
|
||||
propertyFirRef.returnTypeAndAnnotations(FirResolvePhase.TYPES, builder)
|
||||
}
|
||||
|
||||
override val owningProperty: KtKotlinPropertySymbol by propertyFirRef.withFirAndCache { fir ->
|
||||
builder.variableLikeBuilder.buildPropertySymbol(fir)
|
||||
}
|
||||
|
||||
override fun createPointer(): KtSymbolPointer<KtBackingFieldSymbol> {
|
||||
return KtFirBackingFieldSymbolPointer(owningProperty.createPointer())
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.idea.frontend.api.fir.symbols.pointers
|
||||
|
||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.KtFirAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirKotlinPropertySymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtBackingFieldSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtKotlinPropertySymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.pointers.KtSymbolPointer
|
||||
|
||||
internal class KtFirBackingFieldSymbolPointer(
|
||||
private val propertySymbolPointer: KtSymbolPointer<KtKotlinPropertySymbol>,
|
||||
) : KtSymbolPointer<KtBackingFieldSymbol>() {
|
||||
override fun restoreSymbol(analysisSession: KtAnalysisSession): KtBackingFieldSymbol? {
|
||||
require(analysisSession is KtFirAnalysisSession)
|
||||
val propertySymbol = propertySymbolPointer.restoreSymbol(analysisSession) ?: return null
|
||||
check(propertySymbol is KtFirKotlinPropertySymbol)
|
||||
return propertySymbol.firRef.withFir { firProperty ->
|
||||
analysisSession.firSymbolBuilder.variableLikeBuilder.buildBackingFieldSymbolByProperty(firProperty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -81,6 +81,9 @@ internal object FirReferenceResolveHelper {
|
||||
|
||||
fun FirReference.toTargetSymbol(session: FirSession, symbolBuilder: KtSymbolByFirBuilder): Collection<KtSymbol> {
|
||||
return when (this) {
|
||||
is FirBackingFieldReference -> {
|
||||
listOfNotNull(symbolBuilder.variableLikeBuilder.buildBackingFieldSymbol(resolvedSymbol))
|
||||
}
|
||||
is FirResolvedNamedReference -> {
|
||||
val fir = when (val symbol = resolvedSymbol) {
|
||||
is SyntheticPropertySymbol -> {
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
var x: Int = 0
|
||||
get() = <caret>field
|
||||
set(value) {
|
||||
field = value
|
||||
}
|
||||
+5
@@ -25,6 +25,11 @@ public class SymbolByReferenceTestGenerated extends AbstractSymbolByReferenceTes
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
@TestMetadata("accessorField.kt")
|
||||
public void testAccessorField() throws Exception {
|
||||
runTest("idea/idea-frontend-fir/testData/symbolByReference/accessorField.kt");
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInSymbolByReference() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-frontend-fir/testData/symbolByReference"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user