FIR IDE: add completion for annotations

This commit is contained in:
Ilya Kirillov
2021-05-17 13:39:05 +02:00
committed by TeamCityServer
parent 6b450e6fdf
commit d76ec9db50
23 changed files with 161 additions and 16 deletions
@@ -1,3 +1,4 @@
// FIR_COMPARISON
@Target(AnnotationTarget.TYPE)
annotation class AnnType
@@ -1,3 +1,4 @@
// FIR_COMPARISON
annotation class Hello
val v = 1
@@ -1,3 +1,4 @@
// FIR_COMPARISON
annotation class Hello
val v = 1
@@ -1,3 +1,4 @@
// FIR_COMPARISON
annotation class Hello
val v = 1
@@ -1,3 +1,4 @@
// FIR_COMPARISON
annotation class Hello
val v = 1
@@ -1,3 +1,4 @@
// FIR_COMPARISON
annotation class Hello
val v = 1
@@ -1,3 +1,4 @@
// FIR_COMPARISON
annotation class Hello
val v = 1
@@ -1,3 +1,4 @@
// FIR_COMPARISON
annotation class Hello
val v = 1
@@ -1,3 +1,4 @@
// FIR_COMPARISON
annotation class SHello
val v = 1
@@ -1,3 +1,4 @@
// FIR_COMPARISON
val v = 1
fun foo(@[S<caret>) { }
@@ -1,3 +1,4 @@
// FIR_COMPARISON
val v = 1
fun foo(@[volatile S<caret>) { }
@@ -1,3 +1,4 @@
// FIR_COMPARISON
annotation class Hello
val v = 1
@@ -1,3 +1,4 @@
// FIR_COMPARISON
annotation class Hello
val v = 1
@@ -1,3 +1,4 @@
// FIR_COMPARISON
annotation class Hello
val v = 1
@@ -17,6 +17,8 @@ import org.jetbrains.kotlin.idea.completion.context.FirExpressionNameReferencePo
import org.jetbrains.kotlin.idea.completion.context.FirPositionCompletionContextDetector
import org.jetbrains.kotlin.idea.completion.context.FirTypeNameReferencePositionContext
import org.jetbrains.kotlin.idea.completion.context.FirUnknownPositionContext
import org.jetbrains.kotlin.idea.completion.contributors.*
import org.jetbrains.kotlin.idea.completion.contributors.FirAnnotationCompletionContributor
import org.jetbrains.kotlin.idea.completion.contributors.FirCallableCompletionContributor
import org.jetbrains.kotlin.idea.completion.contributors.FirClassifierCompletionContributor
import org.jetbrains.kotlin.idea.completion.contributors.FirKeywordCompletionContributor
@@ -57,6 +59,7 @@ private object KotlinFirCompletionProvider : CompletionProvider<CompletionParame
val keywordContributor = FirKeywordCompletionContributor(basicContext)
val callableContributor = FirCallableCompletionContributor(basicContext)
val classifierContributor = FirClassifierCompletionContributor(basicContext)
val annotationsContributor = FirAnnotationCompletionContributor(basicContext)
when (positionContext) {
is FirExpressionNameReferencePositionContext -> {
@@ -68,6 +71,12 @@ private object KotlinFirCompletionProvider : CompletionProvider<CompletionParame
complete(keywordContributor, positionContext)
complete(classifierContributor, positionContext)
}
is FirAnnotationTypeNameReferencePositionContext -> {
complete(keywordContributor, positionContext)
complete(annotationsContributor, positionContext)
}
is FirUnknownPositionContext -> {
complete(keywordContributor, positionContext)
}
@@ -11,10 +11,7 @@ import org.jetbrains.kotlin.idea.frontend.api.analyse
import org.jetbrains.kotlin.idea.frontend.api.analyseInDependedAnalysisSession
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtLabelReferenceExpression
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
import org.jetbrains.kotlin.psi.KtUserType
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
internal sealed class FirRawPositionCompletionContext {
@@ -34,6 +31,15 @@ internal class FirTypeNameReferencePositionContext(
override val explicitReceiver: KtExpression?
) : FirNameReferencePositionContext()
internal class FirAnnotationTypeNameReferencePositionContext(
override val position: PsiElement,
override val reference: KtSimpleNameReference,
override val nameExpression: KtSimpleNameExpression,
override val explicitReceiver: KtExpression?,
val annotationEntry: KtAnnotationEntry,
) : FirNameReferencePositionContext()
internal class FirExpressionNameReferencePositionContext(
override val position: PsiElement,
override val reference: KtSimpleNameReference,
@@ -55,13 +61,34 @@ internal object FirPositionCompletionContextDetector {
?: return FirUnknownPositionContext(position)
val explicitReceiver = nameExpression.getReceiverExpression()
return if (nameExpression.parent is KtUserType) {
FirTypeNameReferencePositionContext(position, reference, nameExpression, explicitReceiver)
} else {
FirExpressionNameReferencePositionContext(position, reference, nameExpression, explicitReceiver)
return when (val parent = nameExpression.parent) {
is KtUserType -> {
detectForTypeContext(parent, position, reference, nameExpression, explicitReceiver)
}
else -> {
FirExpressionNameReferencePositionContext(position, reference, nameExpression, explicitReceiver)
}
}
}
private fun detectForTypeContext(
userType: KtUserType,
position: PsiElement,
reference: KtSimpleNameReference,
nameExpression: KtSimpleNameExpression,
explicitReceiver: KtExpression?
): FirNameReferencePositionContext {
val annotationEntry = userType.annotationEntry()
?: return FirTypeNameReferencePositionContext(position, reference, nameExpression, explicitReceiver)
return FirAnnotationTypeNameReferencePositionContext(position, reference, nameExpression, explicitReceiver, annotationEntry)
}
private fun KtUserType.annotationEntry(): KtAnnotationEntry? {
val typeReference = (parent as? KtTypeReference)?.takeIf { it.typeElement == this }
val constructorCall = (typeReference?.parent as? KtConstructorCalleeExpression)?.takeIf { it.typeReference == typeReference }
return (constructorCall?.parent as? KtAnnotationEntry)?.takeIf { it.calleeExpression == constructorCall }
}
inline fun analyseInContext(
basicContext: FirBasicCompletionContext,
positionContext: FirRawPositionCompletionContext,
@@ -9,20 +9,21 @@ import org.jetbrains.kotlin.idea.completion.checkers.CompletionVisibilityChecker
import org.jetbrains.kotlin.idea.completion.context.FirBasicCompletionContext
import org.jetbrains.kotlin.idea.completion.context.FirNameReferenceRawPositionContext
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtClassLikeSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtClassifierSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtTypeParameterSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtNamedSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithMembers
import org.jetbrains.kotlin.idea.frontend.api.types.KtClassType
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtEnumEntry
import org.jetbrains.kotlin.psi.KtExpression
internal class FirClassifierCompletionContributor(
internal open class FirClassifierCompletionContributor(
basicContext: FirBasicCompletionContext,
) : FirContextCompletionContributorBase<FirNameReferenceRawPositionContext>(basicContext) {
protected open fun KtAnalysisSession.filterClassifiers(classifierSymbol: KtClassifierSymbol): Boolean = true
override fun KtAnalysisSession.complete(positionContext: FirNameReferenceRawPositionContext) {
val visibilityChecker = CompletionVisibilityChecker.create(basicContext, positionContext)
@@ -44,6 +45,7 @@ internal class FirClassifierCompletionContributor(
val symbol = reference.resolveToSymbol() as? KtSymbolWithMembers ?: return
symbol.getMemberScope()
.getClassifierSymbols(scopeNameFilter)
.filter { filterClassifiers(it) }
.filter { with(visibilityChecker) { isVisible(it) } }
.forEach { addClassifierSymbolToCompletion(it, insertFqName = false) }
}
@@ -55,6 +57,7 @@ internal class FirClassifierCompletionContributor(
val implicitScopes = originalKtFile.getScopeContextForPosition(positionContext.nameExpression).scopes
val classesFromScopes = implicitScopes
.getClassifierSymbols(scopeNameFilter)
.filter { filterClassifiers(it) }
.filter { with(visibilityChecker) { isVisible(it) } }
classesFromScopes.forEach { addClassifierSymbolToCompletion(it, insertFqName = true) }
@@ -62,7 +65,33 @@ internal class FirClassifierCompletionContributor(
val kotlinClassesFromIndices = indexHelper.getKotlinClasses(scopeNameFilter, psiFilter = { it !is KtEnumEntry })
kotlinClassesFromIndices.asSequence()
.map { it.getSymbol() as KtClassifierSymbol }
.filter { filterClassifiers(it) }
.filter { with(visibilityChecker) { isVisible(it) } }
.forEach { addClassifierSymbolToCompletion(it, insertFqName = true) }
}
}
internal class FirAnnotationCompletionContributor(
basicContext: FirBasicCompletionContext
) : FirClassifierCompletionContributor(basicContext) {
override fun KtAnalysisSession.filterClassifiers(classifierSymbol: KtClassifierSymbol): Boolean = when (classifierSymbol) {
is KtAnonymousObjectSymbol -> false
is KtTypeParameterSymbol -> false
is KtNamedClassOrObjectSymbol -> when (classifierSymbol.classKind) {
KtClassKind.ANNOTATION_CLASS -> true
KtClassKind.ENUM_CLASS -> false
KtClassKind.ENUM_ENTRY -> false
KtClassKind.ANONYMOUS_OBJECT -> false
KtClassKind.CLASS, KtClassKind.OBJECT, KtClassKind.COMPANION_OBJECT, KtClassKind.INTERFACE -> {
// TODO show class if nested classifier is annotation class
// classifierSymbol.getDeclaredMemberScope().getClassifierSymbols().any { filterClassifiers(it) }
false
}
}
is KtTypeAliasSymbol -> {
val expendedClass = (classifierSymbol.expandedType as? KtClassType)?.classSymbol
expendedClass?.let { filterClassifiers(it) } == true
}
}
}
@@ -33,10 +33,14 @@ sealed class KtClassLikeSymbol : KtClassifierSymbol(), KtSymbolWithKind {
}
abstract class KtTypeAliasSymbol : KtClassLikeSymbol(), KtNamedSymbol {
abstract override val classIdIfNonLocal: ClassId
final override val symbolKind: KtSymbolKind get() = KtSymbolKind.TOP_LEVEL
/**
* Returns type from right-hand site of type alias
* If type alias has type parameters, then those type parameters will be present in result type
*/
abstract val expandedType: KtType
abstract override fun createPointer(): KtSymbolPointer<KtTypeAliasSymbol>
}
@@ -154,7 +154,7 @@ internal class KtSymbolByFirBuilder private constructor(
}
fun buildTypeAliasSymbol(fir: FirTypeAlias): KtFirTypeAliasSymbol {
return symbolsCache.cache(fir) { KtFirTypeAliasSymbol(fir, resolveState, token) }
return symbolsCache.cache(fir) { KtFirTypeAliasSymbol(fir, resolveState, token, this@KtSymbolByFirBuilder) }
}
fun buildTypeParameterSymbol(fir: FirTypeParameter): KtFirTypeParameterSymbol {
@@ -6,27 +6,38 @@
package org.jetbrains.kotlin.idea.frontend.api.fir.symbols
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.declarations.FirTypeAlias
import org.jetbrains.kotlin.idea.fir.findPsi
import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState
import org.jetbrains.kotlin.idea.frontend.api.fir.KtSymbolByFirBuilder
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.KtTypeAliasSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.pointers.KtPsiBasedSymbolPointer
import org.jetbrains.kotlin.idea.frontend.api.symbols.pointers.KtSymbolPointer
import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
internal class KtFirTypeAliasSymbol(
fir: FirTypeAlias,
resolveState: FirModuleResolveState,
override val token: ValidityToken
override val token: ValidityToken,
_builder: KtSymbolByFirBuilder,
) : KtTypeAliasSymbol(), KtFirSymbol<FirTypeAlias> {
private val builder by weakRef(_builder)
override val firRef = firRef(fir, resolveState)
override val psi: PsiElement? by firRef.withFirAndCache { fir -> fir.findPsi(fir.moduleData.session) }
override val name: Name get() = firRef.withFir { it.name }
override val classIdIfNonLocal: ClassId get() = firRef.withFir { it.symbol.classId }
override val expandedType: KtType by firRef.withFirAndCache(FirResolvePhase.SUPER_TYPES) { fir ->
builder.typeBuilder.buildKtType(fir.expandedTypeRef)
}
override fun createPointer(): KtSymbolPointer<KtTypeAliasSymbol> {
KtPsiBasedSymbolPointer.createForSymbolFromSource(this)?.let { return it }
TODO("Creating symbols for library typealiases is not supported yet")
@@ -0,0 +1,46 @@
class X<T>
typealias Y<Z> = X<Z>
// RESULT
/*
KtFirTypeParameterSymbol:
isReified: false
name: T
origin: SOURCE
upperBounds: [kotlin/Any?]
variance: INVARIANT
KtFirNamedClassOrObjectSymbol:
annotationClassIds: []
annotations: []
classIdIfNonLocal: X
classKind: CLASS
companionObject: null
isData: false
isExternal: false
isFun: false
isInline: false
isInner: false
modality: FINAL
name: X
origin: SOURCE
superTypes: [[] kotlin/Any]
symbolKind: TOP_LEVEL
typeParameters: [KtFirTypeParameterSymbol(T)]
visibility: Public
KtFirTypeParameterSymbol:
isReified: false
name: Z
origin: SOURCE
upperBounds: [kotlin/Any?]
variance: INVARIANT
KtFirTypeAliasSymbol:
classIdIfNonLocal: Y
expandedType: X<Z>
name: Y
origin: SOURCE
symbolKind: TOP_LEVEL
*/
@@ -119,6 +119,11 @@ public class SymbolByPsiTestGenerated extends AbstractSymbolByPsiTest {
runTest("idea/idea-frontend-fir/testData/symbols/symbolByPsi/topLevelProperties.kt");
}
@TestMetadata("typeAlias.kt")
public void testTypeAlias() throws Exception {
runTest("idea/idea-frontend-fir/testData/symbols/symbolByPsi/typeAlias.kt");
}
@TestMetadata("typeAnnotations.kt")
public void testTypeAnnotations() throws Exception {
runTest("idea/idea-frontend-fir/testData/symbols/symbolByPsi/typeAnnotations.kt");