diff --git a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/KtAnalysisSession.kt b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/KtAnalysisSession.kt index 739031537e2..92d52f8d286 100644 --- a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/KtAnalysisSession.kt +++ b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/KtAnalysisSession.kt @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.psi.KtFile public abstract class KtAnalysisSession(final override val token: ValidityToken) : ValidityTokenOwner, KtSmartCastProviderMixIn, KtCallResolverMixIn, + KtSamResolverMixIn, KtDiagnosticProviderMixIn, KtScopeProviderMixIn, KtCompletionCandidateCheckerMixIn, @@ -75,6 +76,9 @@ public abstract class KtAnalysisSession(final override val token: ValidityToken) internal val callResolver: KtCallResolver get() = callResolverImpl protected abstract val callResolverImpl: KtCallResolver + internal val samResolver: KtSamResolver get() = samResolverImpl + protected abstract val samResolverImpl: KtSamResolver + internal val completionCandidateChecker: KtCompletionCandidateChecker get() = completionCandidateCheckerImpl protected abstract val completionCandidateCheckerImpl: KtCompletionCandidateChecker diff --git a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/components/KtSamResolver.kt b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/components/KtSamResolver.kt new file mode 100644 index 00000000000..14afec980b4 --- /dev/null +++ b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/components/KtSamResolver.kt @@ -0,0 +1,22 @@ +/* + * 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.idea.frontend.api.components + +import org.jetbrains.kotlin.idea.frontend.api.symbols.KtClassLikeSymbol +import org.jetbrains.kotlin.idea.frontend.api.symbols.KtSamConstructorSymbol +import org.jetbrains.kotlin.name.ClassId + +public abstract class KtSamResolver : KtAnalysisSessionComponent() { + public abstract fun getSamConstructor(ktClassLikeSymbol: KtClassLikeSymbol): KtSamConstructorSymbol? +} + +public interface KtSamResolverMixIn : KtAnalysisSessionMixIn { + /** + * Returns [KtSamConstructorSymbol] if the given [KtClassLikeSymbol] is a functional interface type, a.k.a. SAM. + */ + public fun KtClassLikeSymbol.getSamConstructor(): KtSamConstructorSymbol? = + analysisSession.samResolver.getSamConstructor(this) +} diff --git a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/symbols/DebugSymbolRenderer.kt b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/symbols/DebugSymbolRenderer.kt index 306af19e554..1c2ffc12fe4 100644 --- a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/symbols/DebugSymbolRenderer.kt +++ b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/symbols/DebugSymbolRenderer.kt @@ -85,6 +85,7 @@ public object DebugSymbolRenderer { val symbolTag = when (value) { is KtClassLikeSymbol -> renderValue(value.classIdIfNonLocal ?: "/${value.name}") is KtFunctionSymbol -> renderValue(value.callableIdIfNonLocal ?: "/${value.name}") + is KtSamConstructorSymbol -> renderValue(value.callableIdIfNonLocal ?: "/${value.name}") is KtConstructorSymbol -> "" is KtNamedSymbol -> renderValue(value.name) is KtPropertyGetterSymbol -> "" diff --git a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/symbols/KtFunctionLikeSymbol.kt b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/symbols/KtFunctionLikeSymbol.kt index 7a90542a2cd..8134f14f8cb 100644 --- a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/symbols/KtFunctionLikeSymbol.kt +++ b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/symbols/KtFunctionLikeSymbol.kt @@ -9,7 +9,6 @@ import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.* import org.jetbrains.kotlin.idea.frontend.api.symbols.pointers.KtSymbolPointer import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.name.ClassId -import org.jetbrains.kotlin.name.FqName public abstract class KtFunctionLikeSymbol : KtCallableSymbol(), KtSymbolWithKind { public abstract val valueParameters: List @@ -31,6 +30,12 @@ public abstract class KtAnonymousFunctionSymbol : KtFunctionLikeSymbol() { abstract override fun createPointer(): KtSymbolPointer } +public abstract class KtSamConstructorSymbol : KtFunctionLikeSymbol(), KtNamedSymbol { + final override val symbolKind: KtSymbolKind get() = KtSymbolKind.SAM_CONSTRUCTOR + + abstract override fun createPointer(): KtSymbolPointer +} + public abstract class KtFunctionSymbol : KtFunctionLikeSymbol(), KtNamedSymbol, KtPossibleMemberSymbol, @@ -64,4 +69,4 @@ public abstract class KtConstructorSymbol : KtFunctionLikeSymbol(), final override val receiverType: KtTypeAndAnnotations? get() = null abstract override fun createPointer(): KtSymbolPointer -} \ No newline at end of file +} diff --git a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/symbols/KtSymbol.kt b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/symbols/KtSymbol.kt index 4a35de9445f..2f37a13179d 100644 --- a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/symbols/KtSymbol.kt +++ b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/symbols/KtSymbol.kt @@ -67,6 +67,12 @@ public enum class KtSymbolOrigin { */ JAVA, + /** + * A synthetic function that is called as a lambda argument when creating a SAM interface object, e.g., + * ``` + * val isEven = IntPredicate { it % 2 == 0 } + * ``` + */ SAM_CONSTRUCTOR, /** @@ -102,4 +108,4 @@ public enum class KtSymbolOrigin { * @see KtBackingFieldSymbol */ PROPERTY_BACKING_FIELD, -} \ No newline at end of file +} diff --git a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/symbols/markers/KtSymbolWithKind.kt b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/symbols/markers/KtSymbolWithKind.kt index 63a2bc82102..828b0a5cb1c 100644 --- a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/symbols/markers/KtSymbolWithKind.kt +++ b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/symbols/markers/KtSymbolWithKind.kt @@ -15,6 +15,10 @@ public interface KtSymbolWithKind : KtSymbol { } public enum class KtSymbolKind { - TOP_LEVEL, MEMBER, LOCAL, ACCESSOR + TOP_LEVEL, + MEMBER, + LOCAL, + ACCESSOR, + SAM_CONSTRUCTOR, } diff --git a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/symbols/pointers/CouldNotCreateSymbolPointerException.kt b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/symbols/pointers/SymbolPointerExceptions.kt similarity index 63% rename from idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/symbols/pointers/CouldNotCreateSymbolPointerException.kt rename to idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/symbols/pointers/SymbolPointerExceptions.kt index 2ef65de5db6..79eb72d3fa2 100644 --- a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/symbols/pointers/CouldNotCreateSymbolPointerException.kt +++ b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/symbols/pointers/SymbolPointerExceptions.kt @@ -6,4 +6,9 @@ package org.jetbrains.kotlin.idea.frontend.api.symbols.pointers public class CanNotCreateSymbolPointerForLocalLibraryDeclarationException(description: String) : - IllegalStateException("Could not create a symbol pointer for local symbol $description") \ No newline at end of file + IllegalStateException("Could not create a symbol pointer for local symbol $description") + +public class WrongSymbolForSamConstructor(symbolKind: String) : + IllegalStateException( + "For symbol with kind = KtSymbolKind.SAM_CONSTRUCTOR, KtSamConstructorSymbol should be created, but was $symbolKind" + ) diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/KtFirAnalysisSession.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/KtFirAnalysisSession.kt index 0fd6ac4bd14..de4b72ac3a5 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/KtFirAnalysisSession.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/KtFirAnalysisSession.kt @@ -50,6 +50,8 @@ private constructor( override val callResolverImpl = KtFirCallResolver(this, token) + override val samResolverImpl = KtFirSamResolver(this, token) + override val scopeProviderImpl by threadLocal { KtFirScopeProvider(this, firSymbolBuilder, project, firResolveState, token) } override val symbolProviderImpl = diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/KtSymbolByFirBuilder.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/KtSymbolByFirBuilder.kt index 3c9701bfe4a..1452f21bda5 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/KtSymbolByFirBuilder.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/KtSymbolByFirBuilder.kt @@ -191,7 +191,13 @@ internal class KtSymbolByFirBuilder private constructor( inner class FunctionLikeSymbolBuilder { fun buildFunctionLikeSymbol(fir: FirFunction): KtFunctionLikeSymbol { return when (fir) { - is FirSimpleFunction -> buildFunctionSymbol(fir) + is FirSimpleFunction -> { + if (fir.origin == FirDeclarationOrigin.SamConstructor) { + buildSamConstructorSymbol(fir) + } else { + buildFunctionSymbol(fir) + } + } is FirConstructor -> buildConstructorSymbol(fir) is FirAnonymousFunction -> buildAnonymousFunctionSymbol(fir) else -> throwUnexpectedElementError(fir) @@ -199,6 +205,7 @@ internal class KtSymbolByFirBuilder private constructor( } fun buildFunctionSymbol(fir: FirSimpleFunction): KtFirFunctionSymbol { + check(fir.origin != FirDeclarationOrigin.SamConstructor) return symbolsCache.cache(fir) { KtFirFunctionSymbol(fir, resolveState, token, this@KtSymbolByFirBuilder) } } @@ -212,6 +219,11 @@ internal class KtSymbolByFirBuilder private constructor( KtFirConstructorSymbol(originalFir, resolveState, token, this@KtSymbolByFirBuilder) } } + + fun buildSamConstructorSymbol(fir: FirSimpleFunction): KtFirSamConstructorSymbol { + check(fir.origin == FirDeclarationOrigin.SamConstructor) + return symbolsCache.cache(fir) { KtFirSamConstructorSymbol(fir, resolveState, token, this@KtSymbolByFirBuilder) } + } } inner class VariableLikeSymbolBuilder { diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirSamResolver.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirSamResolver.kt new file mode 100644 index 00000000000..3006a572816 --- /dev/null +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirSamResolver.kt @@ -0,0 +1,66 @@ +/* + * 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.idea.frontend.api.fir.components + +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin +import org.jetbrains.kotlin.fir.declarations.FirRegularClass +import org.jetbrains.kotlin.fir.declarations.FirResolvePhase +import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction +import org.jetbrains.kotlin.fir.resolve.ScopeSession +import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformer +import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirBodyResolveTransformer +import org.jetbrains.kotlin.idea.frontend.api.components.KtSamResolver +import org.jetbrains.kotlin.idea.frontend.api.fir.KtFirAnalysisSession +import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.pointers.getClassLikeSymbol +import org.jetbrains.kotlin.idea.frontend.api.symbols.KtClassLikeSymbol +import org.jetbrains.kotlin.idea.frontend.api.symbols.KtSamConstructorSymbol +import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken + +internal class KtFirSamResolver( + override val analysisSession: KtFirAnalysisSession, + override val token: ValidityToken, +) : KtSamResolver(), KtFirAnalysisSessionComponent { + + override fun getSamConstructor(ktClassLikeSymbol: KtClassLikeSymbol): KtSamConstructorSymbol? { + val classId = ktClassLikeSymbol.classIdIfNonLocal ?: return null + val owner = analysisSession.getClassLikeSymbol(classId) as? FirRegularClass ?: return null + val resolver = LocalSamResolver(analysisSession.rootModuleSession) + return resolver.getSamConstructor(owner)?.let { + analysisSession.firSymbolBuilder.functionLikeBuilder.buildSamConstructorSymbol(it) + } + } + + private class LocalSamResolver( + private val firSession: FirSession, + ) { + private val scopeSession = ScopeSession() + + // TODO: This transformer is not intended for actual transformations and + // created here only to simplify access to SAM resolver in body resolve components + private val stubBodyResolveTransformer = object : FirBodyResolveTransformer( + session = firSession, + phase = FirResolvePhase.BODY_RESOLVE, + implicitTypeOnly = false, + scopeSession = scopeSession, + ) {} + + private val bodyResolveComponents = + FirAbstractBodyResolveTransformer.BodyResolveTransformerComponents( + firSession, + scopeSession, + stubBodyResolveTransformer, + stubBodyResolveTransformer.context, + ) + + // TODO: this doesn't guarantee that the same synthetic function (as a SAM constructor) is created/returned + fun getSamConstructor(firClass: FirRegularClass): FirSimpleFunction? { + val samConstructor = bodyResolveComponents.samResolver.getSamConstructor(firClass) ?: return null + if (samConstructor.origin != FirDeclarationOrigin.SamConstructor) return null + return samConstructor + } + } +} diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirSymbolContainingDeclarationProvider.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirSymbolContainingDeclarationProvider.kt index f2777900333..5b91bfaf2e6 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirSymbolContainingDeclarationProvider.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirSymbolContainingDeclarationProvider.kt @@ -15,7 +15,6 @@ import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken import org.jetbrains.kotlin.idea.frontend.api.components.KtSymbolContainingDeclarationProvider import org.jetbrains.kotlin.idea.frontend.api.fir.KtFirAnalysisSession import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirSymbol -import org.jetbrains.kotlin.idea.frontend.api.fir.utils.firRef import org.jetbrains.kotlin.idea.frontend.api.symbols.* import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolKind import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithKind @@ -43,7 +42,7 @@ internal class KtFirSymbolContainingDeclarationProvider( getContainingDeclarationForLibrarySymbol(symbol) KtSymbolOrigin.PROPERTY_BACKING_FIELD -> getContainingDeclarationForBackingFieldSymbol(symbol) KtSymbolOrigin.INTERSECTION_OVERRIDE -> TODO() - KtSymbolOrigin.SAM_CONSTRUCTOR -> TODO() + KtSymbolOrigin.SAM_CONSTRUCTOR -> null KtSymbolOrigin.DELEGATED -> TODO() } } @@ -121,4 +120,4 @@ internal class KtFirSymbolContainingDeclarationProvider( val containingClass = containingClassId.getCorrespondingToplevelClassOrObjectSymbol() return containingClass ?: error("Class with id $containingClassId should exists") } -} \ No newline at end of file +} diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/KtFirFunctionSymbol.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/KtFirFunctionSymbol.kt index fe7eae553c6..5846b12100c 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/KtFirFunctionSymbol.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/KtFirFunctionSymbol.kt @@ -29,9 +29,7 @@ import org.jetbrains.kotlin.idea.frontend.api.symbols.KtValueParameterSymbol import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtAnnotationCall import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolKind import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtTypeAndAnnotations -import org.jetbrains.kotlin.idea.frontend.api.symbols.pointers.CanNotCreateSymbolPointerForLocalLibraryDeclarationException -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.symbols.pointers.* import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken import org.jetbrains.kotlin.idea.frontend.api.types.KtType import org.jetbrains.kotlin.name.CallableId @@ -118,6 +116,7 @@ internal class KtFirFunctionSymbol( KtSymbolKind.LOCAL -> throw CanNotCreateSymbolPointerForLocalLibraryDeclarationException( callableIdIfNonLocal?.toString() ?: name.asString() ) + KtSymbolKind.SAM_CONSTRUCTOR -> throw WrongSymbolForSamConstructor(this::class.java.simpleName) } } diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/KtFirKotlinPropertySymbol.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/KtFirKotlinPropertySymbol.kt index c9e2d769ccc..4b7ab7f97e0 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/KtFirKotlinPropertySymbol.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/KtFirKotlinPropertySymbol.kt @@ -32,9 +32,7 @@ import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtAnnotationCall import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtConstantValue import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolKind import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtTypeAndAnnotations -import org.jetbrains.kotlin.idea.frontend.api.symbols.pointers.CanNotCreateSymbolPointerForLocalLibraryDeclarationException -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.symbols.pointers.* import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken import org.jetbrains.kotlin.idea.frontend.api.types.KtType import org.jetbrains.kotlin.name.CallableId @@ -127,6 +125,7 @@ internal class KtFirKotlinPropertySymbol( } KtSymbolKind.ACCESSOR -> TODO("Creating symbol for accessors is not supported yet") KtSymbolKind.LOCAL -> throw CanNotCreateSymbolPointerForLocalLibraryDeclarationException(name.asString()) + KtSymbolKind.SAM_CONSTRUCTOR -> throw WrongSymbolForSamConstructor(this::class.java.simpleName) } } diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/KtFirSamConstructorSymbol.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/KtFirSamConstructorSymbol.kt new file mode 100644 index 00000000000..282d1cbe28f --- /dev/null +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/KtFirSamConstructorSymbol.kt @@ -0,0 +1,63 @@ +/* + * 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.idea.frontend.api.fir.symbols + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.fir.declarations.FirResolvePhase +import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction +import org.jetbrains.kotlin.fir.resolve.getHasStableParameterNames +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.symbols.pointers.KtFirSamConstructorSymbolPointer +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.KtSamConstructorSymbol +import org.jetbrains.kotlin.idea.frontend.api.symbols.KtValueParameterSymbol +import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtTypeAndAnnotations +import org.jetbrains.kotlin.idea.frontend.api.symbols.pointers.KtSymbolPointer +import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken +import org.jetbrains.kotlin.name.CallableId +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.Name + +internal class KtFirSamConstructorSymbol( + fir: FirSimpleFunction, + resolveState: FirModuleResolveState, + override val token: ValidityToken, + _builder: KtSymbolByFirBuilder +) : KtSamConstructorSymbol(), KtFirSymbol { + 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 annotatedType: KtTypeAndAnnotations by cached { + firRef.returnTypeAndAnnotations(FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE, builder) + } + + override val valueParameters: List by firRef.withFirAndCache { fir -> + fir.valueParameters.map { valueParameter -> + builder.variableLikeBuilder.buildValueParameterSymbol(valueParameter) + } + } + + override val hasStableParameterNames: Boolean = firRef.withFir { it.getHasStableParameterNames(it.moduleData.session) } + + override val isExtension: Boolean get() = firRef.withFir { it.receiverTypeRef != null } + override val receiverType: KtTypeAndAnnotations? by cached { + firRef.receiverTypeAndAnnotations(builder) + } + + override val callableIdIfNonLocal: CallableId? get() = getCallableIdIfNonLocal() + + override fun createPointer(): KtSymbolPointer { + return firRef.withFir { fir -> + val callableId = fir.symbol.callableId + KtFirSamConstructorSymbolPointer(ClassId(callableId.packageName, callableId.callableName)) + } + } +} diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/KtFirSymbolProvider.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/KtFirSymbolProvider.kt index 5c618f3bc76..d12107af7da 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/KtFirSymbolProvider.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/KtFirSymbolProvider.kt @@ -47,7 +47,13 @@ internal class KtFirSymbolProvider( override fun getFunctionLikeSymbol(psi: KtNamedFunction): KtFunctionLikeSymbol = withValidityAssertion { psi.withFirDeclarationOfType(resolveState) { fir -> when (fir) { - is FirSimpleFunction -> firSymbolBuilder.functionLikeBuilder.buildFunctionSymbol(fir) + is FirSimpleFunction -> { + if (fir.origin == FirDeclarationOrigin.SamConstructor) { + firSymbolBuilder.functionLikeBuilder.buildSamConstructorSymbol(fir) + } else { + firSymbolBuilder.functionLikeBuilder.buildFunctionSymbol(fir) + } + } is FirAnonymousFunction -> firSymbolBuilder.functionLikeBuilder.buildAnonymousFunctionSymbol(fir) else -> error("Unexpected ${fir.renderWithType()}") } diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/pointers/KtFirMemberSymbolPointer.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/pointers/KtFirMemberSymbolPointer.kt index 496cf30af68..7578937a759 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/pointers/KtFirMemberSymbolPointer.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/pointers/KtFirMemberSymbolPointer.kt @@ -8,7 +8,6 @@ package org.jetbrains.kotlin.idea.frontend.api.fir.symbols.pointers import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.FirRegularClass import org.jetbrains.kotlin.fir.resolve.ScopeSession -import org.jetbrains.kotlin.fir.resolve.symbolProvider import org.jetbrains.kotlin.fir.scopes.FirScope import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession @@ -37,7 +36,3 @@ internal abstract class KtFirMemberSymbolPointer( firSession: FirSession ): S? } - -private fun KtFirAnalysisSession.getClassLikeSymbol(classId: ClassId) = - firResolveState.rootModuleSession.symbolProvider.getClassLikeSymbolByFqName(classId)?.fir - diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/pointers/KtFirSamConstructorSymbolPointer.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/pointers/KtFirSamConstructorSymbolPointer.kt new file mode 100644 index 00000000000..f3762443ed4 --- /dev/null +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/pointers/KtFirSamConstructorSymbolPointer.kt @@ -0,0 +1,26 @@ +/* + * 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.idea.frontend.api.fir.symbols.pointers + +import org.jetbrains.kotlin.fir.declarations.FirRegularClass +import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession +import org.jetbrains.kotlin.idea.frontend.api.fir.KtFirAnalysisSession +import org.jetbrains.kotlin.idea.frontend.api.symbols.KtSamConstructorSymbol +import org.jetbrains.kotlin.idea.frontend.api.symbols.pointers.KtSymbolPointer +import org.jetbrains.kotlin.name.ClassId + +internal class KtFirSamConstructorSymbolPointer( + private val ownerClassId: ClassId, +) : KtSymbolPointer() { + override fun restoreSymbol(analysisSession: KtAnalysisSession): KtSamConstructorSymbol? { + require(analysisSession is KtFirAnalysisSession) + val owner = analysisSession.getClassLikeSymbol(ownerClassId) as? FirRegularClass ?: return null + val classSymbol = analysisSession.firSymbolBuilder.classifierBuilder.buildClassLikeSymbol(owner) + with(analysisSession) { + return classSymbol.getSamConstructor() + } + } +} diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/pointers/pointerUtils.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/pointers/pointerUtils.kt index 07b985b62a5..419b49a97ba 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/pointers/pointerUtils.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/pointers/pointerUtils.kt @@ -9,11 +9,14 @@ import org.jetbrains.kotlin.fir.FirRenderer import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.FirDeclaration import org.jetbrains.kotlin.fir.renderWithType +import org.jetbrains.kotlin.fir.resolve.symbolProvider import org.jetbrains.kotlin.fir.scopes.FirScope import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.idea.fir.low.level.api.ideSessionComponents +import org.jetbrains.kotlin.idea.frontend.api.fir.KtFirAnalysisSession import org.jetbrains.kotlin.ir.util.IdSignature +import org.jetbrains.kotlin.name.ClassId internal inline fun FirScope.findDeclarationWithSignature( signature: IdSignature, @@ -50,3 +53,6 @@ internal fun FirDeclaration.createSignature(): IdSignature { return signatureComposer.composeSignature(this) ?: error("Could not compose signature for ${this.renderWithType(FirRenderer.RenderMode.WithResolvePhases)}, looks like it is private or local") } + +internal fun KtFirAnalysisSession.getClassLikeSymbol(classId: ClassId) = + firResolveState.rootModuleSession.symbolProvider.getClassLikeSymbolByFqName(classId)?.fir diff --git a/idea/idea-frontend-fir/testData/symbols/symbolByReference/samConstructor.kt b/idea/idea-frontend-fir/testData/symbols/symbolByReference/samConstructor.kt new file mode 100644 index 00000000000..250ea55ae06 --- /dev/null +++ b/idea/idea-frontend-fir/testData/symbols/symbolByReference/samConstructor.kt @@ -0,0 +1 @@ +val greeter = Runnable { println("Howdy") } diff --git a/idea/idea-frontend-fir/testData/symbols/symbolByReference/samConstructor.txt b/idea/idea-frontend-fir/testData/symbols/symbolByReference/samConstructor.txt new file mode 100644 index 00000000000..46968b3a015 --- /dev/null +++ b/idea/idea-frontend-fir/testData/symbols/symbolByReference/samConstructor.txt @@ -0,0 +1,13 @@ +KtFirSamConstructorSymbol: + annotatedType: [] java/lang/Runnable + callableIdIfNonLocal: java/lang/Runnable + hasStableParameterNames: true + isExtension: false + name: Runnable + origin: SAM_CONSTRUCTOR + receiverType: null + symbolKind: SAM_CONSTRUCTOR + valueParameters: [ + KtFirValueParameterSymbol(block) + ] + deprecationStatus: null diff --git a/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/fir/frontend/api/symbols/AbstractSymbolTest.kt b/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/fir/frontend/api/symbols/AbstractSymbolTest.kt index 254fcb8fc1b..e92c5a0102b 100644 --- a/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/fir/frontend/api/symbols/AbstractSymbolTest.kt +++ b/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/fir/frontend/api/symbols/AbstractSymbolTest.kt @@ -68,7 +68,7 @@ abstract class AbstractSymbolTest : AbstractHLApiSingleFileTest() { val restored = analyseOnPooledThreadInReadAction(ktFile) { pointersWithRendered.map { (pointer, expectedRender) -> val restored = pointer!!.restoreSymbol() - ?: error("Symbol $expectedRender was not not restored") + ?: error("Symbol $expectedRender was not restored") with(DebugSymbolRenderer) { renderExtra(restored) } } } @@ -84,9 +84,9 @@ abstract class AbstractSymbolTest : AbstractHLApiSingleFileTest() { private object SymbolTestDirectives : SimpleDirectivesContainer() { val DO_NOT_CHECK_SYMBOL_RESTORE by directive( - description = "Symbol restoring for some symbols in current test is not yet supported yet", + description = "Symbol restoring for some symbols in current test is not supported yet", applicability = DirectiveApplicability.Global ) } -private data class PointerWithRenderedSymbol(val pointer: KtSymbolPointer<*>?, val rendered: String) \ No newline at end of file +private data class PointerWithRenderedSymbol(val pointer: KtSymbolPointer<*>?, val rendered: String) diff --git a/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/fir/frontend/api/symbols/SymbolByReferenceTestGenerated.java b/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/fir/frontend/api/symbols/SymbolByReferenceTestGenerated.java index 60afc561b38..8b3f11a674e 100644 --- a/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/fir/frontend/api/symbols/SymbolByReferenceTestGenerated.java +++ b/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/fir/frontend/api/symbols/SymbolByReferenceTestGenerated.java @@ -35,4 +35,10 @@ public class SymbolByReferenceTestGenerated extends AbstractSymbolByReferenceTes public void testConstructorViaTypeAlias() throws Exception { runTest("idea/idea-frontend-fir/testData/symbols/symbolByReference/constructorViaTypeAlias.kt"); } + + @Test + @TestMetadata("samConstructor.kt") + public void testSamConstructor() throws Exception { + runTest("idea/idea-frontend-fir/testData/symbols/symbolByReference/samConstructor.kt"); + } }