FIR: Implement lookup tracking

This commit is contained in:
Ilya Chernikov
2020-12-01 10:38:27 +01:00
parent 7d29ae7cce
commit f8d50d585d
41 changed files with 423 additions and 97 deletions
@@ -33,6 +33,8 @@ data class BuildLogFinder(
private const val GRADLE_LOG = "gradle-build.log" private const val GRADLE_LOG = "gradle-build.log"
private const val DATA_CONTAINER_LOG = "data-container-version-build.log" private const val DATA_CONTAINER_LOG = "data-container-version-build.log"
const val JS_JPS_LOG = "js-jps-build.log" const val JS_JPS_LOG = "js-jps-build.log"
private const val FIR_LOG = "fir-build.log"
private const val GRADLE_FIR_LOG = "gradle-fir-build.log"
private const val SIMPLE_LOG = "build.log" private const val SIMPLE_LOG = "build.log"
fun isJpsLogFile(file: File): Boolean = fun isJpsLogFile(file: File): Boolean =
@@ -46,10 +48,11 @@ data class BuildLogFinder(
isScopeExpansionEnabled && SCOPE_EXPANDING_LOG in files -> SCOPE_EXPANDING_LOG isScopeExpansionEnabled && SCOPE_EXPANDING_LOG in files -> SCOPE_EXPANDING_LOG
isKlibEnabled && KLIB_LOG in files -> KLIB_LOG isKlibEnabled && KLIB_LOG in files -> KLIB_LOG
isJsEnabled && JS_LOG in files -> JS_LOG isJsEnabled && JS_LOG in files -> JS_LOG
isGradleEnabled && isFirEnabled && GRADLE_FIR_LOG in files -> GRADLE_FIR_LOG
isFirEnabled && FIR_LOG in files -> FIR_LOG
isGradleEnabled && GRADLE_LOG in files -> GRADLE_LOG isGradleEnabled && GRADLE_LOG in files -> GRADLE_LOG
isJsEnabled && JS_JPS_LOG in files -> JS_JPS_LOG isJsEnabled && JS_JPS_LOG in files -> JS_JPS_LOG
isDataContainerBuildLogEnabled && DATA_CONTAINER_LOG in files -> DATA_CONTAINER_LOG isDataContainerBuildLogEnabled && DATA_CONTAINER_LOG in files -> DATA_CONTAINER_LOG
isFirEnabled && FIR_LOG in files -> FIR_LOG
SIMPLE_LOG in files -> SIMPLE_LOG SIMPLE_LOG in files -> SIMPLE_LOG
else -> null else -> null
} }
@@ -333,6 +333,7 @@ object KotlinToJVMBytecodeCompiler {
languageVersionSettings, languageVersionSettings,
sourceScope, sourceScope,
librariesScope, librariesScope,
lookupTracker = environment.configuration.get(CommonConfigurationKeys.LOOKUP_TRACKER),
environment::createPackagePartProvider environment::createPackagePartProvider
) { ) {
if (extendedAnalysisMode) { if (extendedAnalysisMode) {
@@ -350,9 +351,6 @@ object KotlinToJVMBytecodeCompiler {
) )
performanceManager?.notifyAnalysisFinished() performanceManager?.notifyAnalysisFinished()
// TODO: maybe we should not do it in presence of errors, but tests at the moment expect lookups to be reported
session.firLookupTracker?.flushLookups()
if (syntaxErrors || firDiagnostics.any { it.severity == Severity.ERROR }) { if (syntaxErrors || firDiagnostics.any { it.severity == Severity.ERROR }) {
return false return false
} }
@@ -11,9 +11,8 @@ import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
import org.jetbrains.kotlin.fir.declarations.FirDeclaration import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.FirFile import org.jetbrains.kotlin.fir.lookupTracker
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
object FirConflictsChecker : FirBasicDeclarationChecker() { object FirConflictsChecker : FirBasicDeclarationChecker() {
@@ -21,7 +20,7 @@ object FirConflictsChecker : FirBasicDeclarationChecker() {
val inspector = FirDeclarationInspector() val inspector = FirDeclarationInspector()
when (declaration) { when (declaration) {
is FirFile -> checkFile(declaration, inspector) is FirFile -> checkFile(declaration, inspector, context)
is FirRegularClass -> checkRegularClass(declaration, inspector) is FirRegularClass -> checkRegularClass(declaration, inspector)
else -> return else -> return
} }
@@ -47,9 +46,21 @@ object FirConflictsChecker : FirBasicDeclarationChecker() {
} }
} }
private fun checkFile(declaration: FirFile, inspector: FirDeclarationInspector) { private fun checkFile(file: FirFile, inspector: FirDeclarationInspector, context: CheckerContext) {
for (it in declaration.declarations) { val lookupTracker = context.session.lookupTracker
inspector.collect(it) for (topLevelDeclaration in file.declarations) {
inspector.collect(topLevelDeclaration)
if (lookupTracker != null) {
when (topLevelDeclaration) {
is FirSimpleFunction -> topLevelDeclaration.name
is FirVariable<*> -> topLevelDeclaration.name
is FirRegularClass -> topLevelDeclaration.name
is FirTypeAlias -> topLevelDeclaration.name
else -> null
}?.let {
lookupTracker.recordLookup(it, file.packageFqName.asString(), topLevelDeclaration.source, file.source)
}
}
} }
} }
@@ -0,0 +1,41 @@
/*
* 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.fir
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils.getLineAndColumnInPsiFile
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.incremental.components.Position
import org.jetbrains.kotlin.incremental.components.ScopeKind
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.SmartList
import java.util.concurrent.ConcurrentHashMap
class IncrementalPassThroughLookupTrackerComponent(
private val lookupTracker: LookupTracker,
private val sourceToFilePath: (FirSourceElement) -> String
) : FirLookupTrackerComponent() {
private val requiresPosition = lookupTracker.requiresPosition
private val sourceToFilePathsCache = ConcurrentHashMap<FirSourceElement, String>()
override fun recordLookup(name: Name, inScopes: List<String>, source: FirSourceElement?, fileSource: FirSourceElement?) {
val definedSource = fileSource ?: source ?: throw AssertionError("Cannot record lookup for \"$name\" without a source")
val path = sourceToFilePathsCache.getOrPut(definedSource) {
sourceToFilePath(definedSource)
}
val position = if (requiresPosition && source != null && source is FirPsiSourceElement<*>) {
getLineAndColumnInPsiFile(source.psi.containingFile, source.psi.textRange).let { Position(it.line, it.column) }
} else Position.NO_POSITION
for (scope in inScopes) {
lookupTracker.record(path, position, scope, ScopeKind.PACKAGE, name.asString())
}
}
override fun recordLookup(name: Name, inScope: String, source: FirSourceElement?, fileSource: FirSourceElement?) {
recordLookup(name, SmartList(inScope), source, fileSource)
}
}
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.session package org.jetbrains.kotlin.fir.session
import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.analysis.CheckersComponent import org.jetbrains.kotlin.fir.analysis.CheckersComponent
@@ -25,6 +26,7 @@ import org.jetbrains.kotlin.fir.resolve.providers.impl.FirTypeResolverImpl
import org.jetbrains.kotlin.fir.resolve.transformers.plugin.GeneratedClassIndex import org.jetbrains.kotlin.fir.resolve.transformers.plugin.GeneratedClassIndex
import org.jetbrains.kotlin.fir.scopes.impl.FirDeclaredMemberScopeProvider import org.jetbrains.kotlin.fir.scopes.impl.FirDeclaredMemberScopeProvider
import org.jetbrains.kotlin.fir.types.FirCorrespondingSupertypesCache import org.jetbrains.kotlin.fir.types.FirCorrespondingSupertypesCache
import org.jetbrains.kotlin.incremental.components.LookupTracker
// -------------------------- Required components -------------------------- // -------------------------- Required components --------------------------
@@ -55,10 +57,20 @@ fun FirSession.registerThreadUnsafeCaches() {
* Resolve components which are same on all platforms * Resolve components which are same on all platforms
*/ */
@OptIn(SessionConfiguration::class) @OptIn(SessionConfiguration::class)
fun FirSession.registerResolveComponents() { fun FirSession.registerResolveComponents(lookupTracker: LookupTracker? = null) {
register(FirQualifierResolver::class, FirQualifierResolverImpl(this)) register(FirQualifierResolver::class, FirQualifierResolverImpl(this))
register(FirTypeResolver::class, FirTypeResolverImpl(this)) register(FirTypeResolver::class, FirTypeResolverImpl(this))
register(CheckersComponent::class, CheckersComponent()) register(CheckersComponent::class, CheckersComponent())
if (lookupTracker != null) {
val firFileToPath: (FirSourceElement) -> String = {
val psiSource = (it as? FirPsiSourceElement<*>) ?: TODO("Not implemented for non-FirPsiSourceElement")
((psiSource.psi as? PsiFile) ?: psiSource.psi.containingFile).virtualFile.path
}
register(
FirLookupTrackerComponent::class,
IncrementalPassThroughLookupTrackerComponent(lookupTracker, firFileToPath)
)
}
} }
/* /*
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.impl.* import org.jetbrains.kotlin.fir.resolve.providers.impl.*
import org.jetbrains.kotlin.fir.resolve.scopes.wrapScopeWithJvmMapped import org.jetbrains.kotlin.fir.resolve.scopes.wrapScopeWithJvmMapped
import org.jetbrains.kotlin.fir.scopes.KotlinScopeProvider import org.jetbrains.kotlin.fir.scopes.KotlinScopeProvider
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.load.java.JavaClassFinderImpl import org.jetbrains.kotlin.load.java.JavaClassFinderImpl
import org.jetbrains.kotlin.load.kotlin.PackagePartProvider import org.jetbrains.kotlin.load.kotlin.PackagePartProvider
import org.jetbrains.kotlin.load.kotlin.VirtualFileFinderFactory import org.jetbrains.kotlin.load.kotlin.VirtualFileFinderFactory
@@ -68,12 +69,13 @@ object FirSessionFactory {
project: Project, project: Project,
dependenciesProvider: FirSymbolProvider? = null, dependenciesProvider: FirSymbolProvider? = null,
languageVersionSettings: LanguageVersionSettings = LanguageVersionSettingsImpl.DEFAULT, languageVersionSettings: LanguageVersionSettings = LanguageVersionSettingsImpl.DEFAULT,
lookupTracker: LookupTracker? = null,
init: FirSessionConfigurator.() -> Unit = {} init: FirSessionConfigurator.() -> Unit = {}
): FirJavaModuleBasedSession { ): FirJavaModuleBasedSession {
return FirJavaModuleBasedSession(moduleInfo, sessionProvider).apply { return FirJavaModuleBasedSession(moduleInfo, sessionProvider).apply {
registerThreadUnsafeCaches() registerThreadUnsafeCaches()
registerCommonComponents(languageVersionSettings) registerCommonComponents(languageVersionSettings)
registerResolveComponents() registerResolveComponents(lookupTracker)
registerJavaSpecificResolveComponents() registerJavaSpecificResolveComponents()
val kotlinScopeProvider = KotlinScopeProvider(::wrapScopeWithJvmMapped) val kotlinScopeProvider = KotlinScopeProvider(::wrapScopeWithJvmMapped)
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider
import org.jetbrains.kotlin.fir.session.FirJvmModuleInfo import org.jetbrains.kotlin.fir.session.FirJvmModuleInfo
import org.jetbrains.kotlin.fir.session.FirSessionFactory import org.jetbrains.kotlin.fir.session.FirSessionFactory
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.load.kotlin.PackagePartProvider import org.jetbrains.kotlin.load.kotlin.PackagePartProvider
import org.jetbrains.kotlin.modules.Module import org.jetbrains.kotlin.modules.Module
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
@@ -24,6 +25,7 @@ fun createSessionWithDependencies(
languageVersionSettings: LanguageVersionSettings, languageVersionSettings: LanguageVersionSettings,
sourceScope: GlobalSearchScope, sourceScope: GlobalSearchScope,
librariesScope: GlobalSearchScope, librariesScope: GlobalSearchScope,
lookupTracker: LookupTracker?,
packagePartProvider: (GlobalSearchScope) -> PackagePartProvider, packagePartProvider: (GlobalSearchScope) -> PackagePartProvider,
sessionConfigurator: FirSessionFactory.FirSessionConfigurator.() -> Unit = {} sessionConfigurator: FirSessionFactory.FirSessionConfigurator.() -> Unit = {}
): FirSession { ): FirSession {
@@ -33,6 +35,7 @@ fun createSessionWithDependencies(
languageVersionSettings, languageVersionSettings,
sourceScope, sourceScope,
librariesScope, librariesScope,
lookupTracker,
packagePartProvider, packagePartProvider,
sessionConfigurator sessionConfigurator
) { ) {
@@ -46,6 +49,7 @@ fun createSessionWithDependencies(
languageVersionSettings: LanguageVersionSettings, languageVersionSettings: LanguageVersionSettings,
sourceScope: GlobalSearchScope, sourceScope: GlobalSearchScope,
librariesScope: GlobalSearchScope, librariesScope: GlobalSearchScope,
lookupTracker: LookupTracker?,
packagePartProvider: (GlobalSearchScope) -> PackagePartProvider, packagePartProvider: (GlobalSearchScope) -> PackagePartProvider,
sessionConfigurator: FirSessionFactory.FirSessionConfigurator.() -> Unit = {} sessionConfigurator: FirSessionFactory.FirSessionConfigurator.() -> Unit = {}
): FirSession { ): FirSession {
@@ -55,6 +59,7 @@ fun createSessionWithDependencies(
languageVersionSettings, languageVersionSettings,
sourceScope, sourceScope,
librariesScope, librariesScope,
lookupTracker,
packagePartProvider, packagePartProvider,
sessionConfigurator sessionConfigurator
) { ) {
@@ -68,6 +73,7 @@ private inline fun createSessionWithDependencies(
languageVersionSettings: LanguageVersionSettings, languageVersionSettings: LanguageVersionSettings,
sourceScope: GlobalSearchScope, sourceScope: GlobalSearchScope,
librariesScope: GlobalSearchScope, librariesScope: GlobalSearchScope,
lookupTracker: LookupTracker?,
packagePartProvider: (GlobalSearchScope) -> PackagePartProvider, packagePartProvider: (GlobalSearchScope) -> PackagePartProvider,
noinline sessionConfigurator: FirSessionFactory.FirSessionConfigurator.() -> Unit, noinline sessionConfigurator: FirSessionFactory.FirSessionConfigurator.() -> Unit,
moduleInfoProvider: (dependencies: List<ModuleInfo>) -> ModuleInfo, moduleInfoProvider: (dependencies: List<ModuleInfo>) -> ModuleInfo,
@@ -84,6 +90,7 @@ private inline fun createSessionWithDependencies(
sourceScope, sourceScope,
project, project,
languageVersionSettings = languageVersionSettings, languageVersionSettings = languageVersionSettings,
lookupTracker = lookupTracker,
init = sessionConfigurator init = sessionConfigurator
) )
} }
@@ -702,6 +702,7 @@ private fun FirConstExpression<*>.setProperType(session: FirSession): FirConstEx
type = kind.expectedConeType(session) type = kind.expectedConeType(session)
} }
replaceTypeRef(typeRef) replaceTypeRef(typeRef)
session.lookupTracker?.recordTypeResolveAsLookup(typeRef, source, null)
return this return this
} }
@@ -8,8 +8,7 @@ package org.jetbrains.kotlin.fir.java.enhancement
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.fir.FirAnnotationContainer import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.builder.FirConstructorBuilder import org.jetbrains.kotlin.fir.declarations.builder.FirConstructorBuilder
import org.jetbrains.kotlin.fir.declarations.builder.FirPrimaryConstructorBuilder import org.jetbrains.kotlin.fir.declarations.builder.FirPrimaryConstructorBuilder
@@ -19,11 +18,9 @@ import org.jetbrains.kotlin.fir.declarations.impl.FirDeclarationStatusImpl
import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl
import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty
import org.jetbrains.kotlin.fir.declarations.synthetic.buildSyntheticProperty import org.jetbrains.kotlin.fir.declarations.synthetic.buildSyntheticProperty
import org.jetbrains.kotlin.fir.initialSignatureAttr
import org.jetbrains.kotlin.fir.java.JavaTypeParameterStack import org.jetbrains.kotlin.fir.java.JavaTypeParameterStack
import org.jetbrains.kotlin.fir.java.declarations.* import org.jetbrains.kotlin.fir.java.declarations.*
import org.jetbrains.kotlin.fir.java.toConeKotlinTypeProbablyFlexible import org.jetbrains.kotlin.fir.java.toConeKotlinTypeProbablyFlexible
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.scopes.jvm.computeJvmDescriptor import org.jetbrains.kotlin.fir.scopes.jvm.computeJvmDescriptor
import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.symbols.impl.*
@@ -85,7 +82,10 @@ class FirSignatureEnhancement(
) )
val newReturnTypeRef = enhanceReturnType(firElement, emptyList(), memberContext, predefinedInfo) val newReturnTypeRef = enhanceReturnType(firElement, emptyList(), memberContext, predefinedInfo)
return firElement.symbol.apply { this.fir.replaceReturnTypeRef(newReturnTypeRef) } return firElement.symbol.apply {
this.fir.replaceReturnTypeRef(newReturnTypeRef)
session.lookupTracker?.recordTypeResolveAsLookup(newReturnTypeRef, this.fir.source, null)
}
} }
is FirField -> { is FirField -> {
if (firElement.returnTypeRef !is FirJavaTypeRef) return original if (firElement.returnTypeRef !is FirJavaTypeRef) return original
@@ -104,4 +104,7 @@ class JavaClassStaticUseSiteScope internal constructor(
override fun mayContainName(name: Name): Boolean { override fun mayContainName(name: Name): Boolean {
return declaredMemberScope.mayContainName(name) || superTypesScopes.any { it.mayContainName(name) } return declaredMemberScope.mayContainName(name) || superTypesScopes.any { it.mayContainName(name) }
} }
override val scopeOwnerLookupNames: List<String>
get() = declaredMemberScope.scopeOwnerLookupNames
} }
@@ -135,6 +135,7 @@ class FirCallResolver(
val typeArguments = (qualifiedAccess as? FirFunctionCall)?.typeArguments.orEmpty() val typeArguments = (qualifiedAccess as? FirFunctionCall)?.typeArguments.orEmpty()
val info = CallInfo( val info = CallInfo(
qualifiedAccess,
if (qualifiedAccess is FirFunctionCall) CallKind.Function else CallKind.VariableAccess, if (qualifiedAccess is FirFunctionCall) CallKind.Function else CallKind.VariableAccess,
name, name,
explicitReceiver, explicitReceiver,
@@ -304,6 +305,7 @@ class FirCallResolver(
} }
val callInfo = CallInfo( val callInfo = CallInfo(
delegatedConstructorCall,
CallKind.DelegatingConstructorCall, CallKind.DelegatingConstructorCall,
name, name,
explicitReceiver = null, explicitReceiver = null,
@@ -352,6 +354,7 @@ class FirCallResolver(
annotationCall.argumentList.transformArguments(transformer, ResolutionMode.ContextDependent) annotationCall.argumentList.transformArguments(transformer, ResolutionMode.ContextDependent)
val callInfo = CallInfo( val callInfo = CallInfo(
annotationCall,
CallKind.Function, CallKind.Function,
name = reference.name, name = reference.name,
explicitReceiver = null, explicitReceiver = null,
@@ -462,6 +465,7 @@ class FirCallResolver(
outerConstraintSystemBuilder: ConstraintSystemBuilder?, outerConstraintSystemBuilder: ConstraintSystemBuilder?,
): CallInfo { ): CallInfo {
return CallInfo( return CallInfo(
callableReferenceAccess,
CallKind.CallableReference, CallKind.CallableReference,
callableReferenceAccess.calleeReference.name, callableReferenceAccess.calleeReference.name,
callableReferenceAccess.explicitReceiver, callableReferenceAccess.explicitReceiver,
@@ -0,0 +1,61 @@
/*
* 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.fir
import org.jetbrains.kotlin.fir.resolve.calls.CallInfo
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.SmartList
abstract class FirLookupTrackerComponent : FirSessionComponent {
abstract fun recordLookup(name: Name, inScopes: List<String>, source: FirSourceElement?, fileSource: FirSourceElement?)
abstract fun recordLookup(name: Name, inScope: String, source: FirSourceElement?, fileSource: FirSourceElement?)
}
fun FirLookupTrackerComponent.recordCallLookup(callInfo: CallInfo, inType: ConeKotlinType) {
if (inType.classId?.isLocal == true) return
val scopes = SmartList(inType.render().replace('/', '.'))
if (inType.classId?.shortClassName?.asString() == "Companion") {
scopes.add(inType.classId!!.outerClassId!!.asString().replace('/', '.'))
}
recordLookup(callInfo.name, scopes, callInfo.callSite.source, callInfo.containingFile.source)
}
fun FirLookupTrackerComponent.recordCallLookup(callInfo: CallInfo, inScopes: List<String>) {
recordLookup(callInfo.name, inScopes, callInfo.callSite.source, callInfo.containingFile.source)
}
fun FirLookupTrackerComponent.recordTypeLookup(typeRef: FirTypeRef, inScopes: List<String>, fileSource: FirSourceElement?) {
if (typeRef is FirUserTypeRef) recordLookup(typeRef.qualifier.first().name, inScopes, typeRef.source, fileSource)
}
fun FirLookupTrackerComponent.recordTypeResolveAsLookup(typeRef: FirTypeRef, source: FirSourceElement?, fileSource: FirSourceElement?) {
if (typeRef !is FirResolvedTypeRef) return // TODO: check if this is the correct behavior
if (source == null && fileSource == null) return // TODO: investigate all cases
fun recordIfValid(type: ConeKotlinType) {
if (type is ConeKotlinErrorType) return // TODO: investigate whether some cases should be recorded, e.g. unresolved
type.classId?.let {
if (!it.isLocal) {
if (it.shortClassName.asString() != "Companion") {
recordLookup(it.shortClassName, it.packageFqName.asString(), source, fileSource)
} else {
recordLookup(it.outerClassId!!.shortClassName, it.outerClassId!!.packageFqName.asString(), source, fileSource)
}
}
}
type.typeArguments.forEach {
if (it is ConeKotlinType) recordIfValid(it)
}
}
recordIfValid(typeRef.type)
}
val FirSession.lookupTracker: FirLookupTrackerComponent? by FirSession.nullableSessionComponentAccessor()
@@ -298,7 +298,9 @@ fun FirCheckedSafeCallSubject.propagateTypeFromOriginalReceiver(nullableReceiver
val expandedReceiverType = if (receiverType is ConeClassLikeType) receiverType.fullyExpandedType(session) else receiverType val expandedReceiverType = if (receiverType is ConeClassLikeType) receiverType.fullyExpandedType(session) else receiverType
replaceTypeRef(typeRef.resolvedTypeFromPrototype(expandedReceiverType.makeConeTypeDefinitelyNotNullOrNotNull())) val resolvedTypeRef = typeRef.resolvedTypeFromPrototype(expandedReceiverType.makeConeTypeDefinitelyNotNullOrNotNull())
replaceTypeRef(resolvedTypeRef)
session.lookupTracker?.recordTypeResolveAsLookup(resolvedTypeRef, source, null)
} }
fun FirSafeCallExpression.propagateTypeFromQualifiedAccessAfterNullCheck( fun FirSafeCallExpression.propagateTypeFromQualifiedAccessAfterNullCheck(
@@ -315,7 +317,9 @@ fun FirSafeCallExpression.propagateTypeFromQualifiedAccessAfterNullCheck(
else else
typeAfterNullCheck typeAfterNullCheck
replaceTypeRef(typeRef.resolvedTypeFromPrototype(resultingType)) val resolvedTypeRef = typeRef.resolvedTypeFromPrototype(resultingType)
replaceTypeRef(resolvedTypeRef)
session.lookupTracker?.recordTypeResolveAsLookup(resolvedTypeRef, source, null)
} }
private fun FirQualifiedAccess.expressionTypeOrUnitForAssignment(): ConeKotlinType? { private fun FirQualifiedAccess.expressionTypeOrUnitForAssignment(): ConeKotlinType? {
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.resolve.calls
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.lookupTracker
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.inference.* import org.jetbrains.kotlin.fir.resolve.inference.*
@@ -20,6 +21,7 @@ import org.jetbrains.kotlin.fir.symbols.StandardClassIds
import org.jetbrains.kotlin.fir.typeContext import org.jetbrains.kotlin.fir.typeContext
import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
import org.jetbrains.kotlin.resolve.calls.inference.addSubtypeConstraintIfCompatible import org.jetbrains.kotlin.resolve.calls.inference.addSubtypeConstraintIfCompatible
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintPosition import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintPosition
@@ -29,6 +31,8 @@ import org.jetbrains.kotlin.types.model.CaptureStatus
import org.jetbrains.kotlin.types.model.TypeSystemCommonSuperTypesContext import org.jetbrains.kotlin.types.model.TypeSystemCommonSuperTypesContext
import org.jetbrains.kotlin.utils.addToStdlib.runIf import org.jetbrains.kotlin.utils.addToStdlib.runIf
val SAM_LOOKUP_NAME = Name.special("<SAM-CONSTRUCTOR>")
fun Candidate.resolveArgumentExpression( fun Candidate.resolveArgumentExpression(
csBuilder: ConstraintSystemBuilder, csBuilder: ConstraintSystemBuilder,
argument: FirExpression, argument: FirExpression,
@@ -391,6 +395,7 @@ private fun checkApplicabilityForArgumentType(
} }
internal fun Candidate.resolveArgument( internal fun Candidate.resolveArgument(
callInfo: CallInfo,
argument: FirExpression, argument: FirExpression,
parameter: FirValueParameter?, parameter: FirValueParameter?,
isReceiver: Boolean, isReceiver: Boolean,
@@ -399,7 +404,8 @@ internal fun Candidate.resolveArgument(
) { ) {
argument.resultType.ensureResolvedTypeDeclaration(context.session) argument.resultType.ensureResolvedTypeDeclaration(context.session)
val expectedType = prepareExpectedType(context.session, context.bodyResolveComponents.scopeSession, argument, parameter, context) val expectedType =
prepareExpectedType(context.session, context.bodyResolveComponents.scopeSession, callInfo, argument, parameter, context)
resolveArgumentExpression( resolveArgumentExpression(
this.system.getBuilder(), this.system.getBuilder(),
argument, argument,
@@ -415,13 +421,33 @@ internal fun Candidate.resolveArgument(
private fun Candidate.prepareExpectedType( private fun Candidate.prepareExpectedType(
session: FirSession, session: FirSession,
scopeSession: ScopeSession, scopeSession: ScopeSession,
callInfo: CallInfo,
argument: FirExpression, argument: FirExpression,
parameter: FirValueParameter?, parameter: FirValueParameter?,
context: ResolutionContext context: ResolutionContext
): ConeKotlinType? { ): ConeKotlinType? {
if (parameter == null) return null if (parameter == null) return null
val basicExpectedType = argument.getExpectedTypeForSAMConversion(parameter/*, LanguageVersionSettings*/) val basicExpectedType = argument.getExpectedTypeForSAMConversion(parameter/*, LanguageVersionSettings*/)
val expectedType = getExpectedTypeWithSAMConversion(session, scopeSession, argument, basicExpectedType, context) ?: basicExpectedType
val expectedType =
getExpectedTypeWithSAMConversion(session, scopeSession, argument, basicExpectedType, context)?.also {
session.lookupTracker?.let { lookupTracker ->
parameter.returnTypeRef.coneType.lowerBoundIfFlexible().classId?.takeIf { !it.isLocal }?.let { classId ->
lookupTracker.recordLookup(
SAM_LOOKUP_NAME,
classId.asString(),
callInfo.callSite.source,
callInfo.containingFile.source
)
lookupTracker.recordLookup(
classId.shortClassName,
classId.packageFqName.asString(),
callInfo.callSite.source,
callInfo.containingFile.source
)
}
}
} ?: basicExpectedType
return this.substitutor.substituteOrSelf(expectedType) return this.substitutor.substituteOrSelf(expectedType)
} }
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.resolve.calls package org.jetbrains.kotlin.fir.resolve.calls
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirDeclaration import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFile import org.jetbrains.kotlin.fir.declarations.FirFile
@@ -35,6 +36,7 @@ import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
data class CallInfo( data class CallInfo(
val callSite: FirElement,
val callKind: CallKind, val callKind: CallKind,
val name: Name, val name: Name,
@@ -144,6 +144,7 @@ internal object CheckArguments : CheckerStage() {
for (argument in callInfo.arguments) { for (argument in callInfo.arguments) {
val parameter = argumentMapping[argument] val parameter = argumentMapping[argument]
candidate.resolveArgument( candidate.resolveArgument(
callInfo,
argument, argument,
parameter, parameter,
isReceiver = false, isReceiver = false,
@@ -15,7 +15,6 @@ import org.jetbrains.kotlin.fir.resolve.DoubleColonLHS
import org.jetbrains.kotlin.fir.resolve.FirTowerDataContext import org.jetbrains.kotlin.fir.resolve.FirTowerDataContext
import org.jetbrains.kotlin.fir.resolve.calls.* import org.jetbrains.kotlin.fir.resolve.calls.*
import org.jetbrains.kotlin.fir.scopes.FirScope import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.impl.FirImplicitBuiltinTypeRef import org.jetbrains.kotlin.fir.types.impl.FirImplicitBuiltinTypeRef
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
@@ -44,18 +44,18 @@ internal class TowerLevelHandler {
when (info.callKind) { when (info.callKind) {
CallKind.VariableAccess -> { CallKind.VariableAccess -> {
processResult += towerLevel.processPropertiesByName(info.name, processor) processResult += towerLevel.processPropertiesByName(info, processor)
if (!collector.isSuccess() && towerLevel is ScopeTowerLevel && towerLevel.extensionReceiver == null) { if (!collector.isSuccess() && towerLevel is ScopeTowerLevel && towerLevel.extensionReceiver == null) {
processResult += towerLevel.processObjectsByName(info.name, processor) processResult += towerLevel.processObjectsByName(info, processor)
} }
} }
CallKind.Function -> { CallKind.Function -> {
processResult += towerLevel.processFunctionsByName(info.name, processor) processResult += towerLevel.processFunctionsByName(info, processor)
} }
CallKind.CallableReference -> { CallKind.CallableReference -> {
processResult += towerLevel.processFunctionsByName(info.name, processor) processResult += towerLevel.processFunctionsByName(info, processor)
processResult += towerLevel.processPropertiesByName(info.name, processor) processResult += towerLevel.processPropertiesByName(info, processor)
} }
else -> { else -> {
throw AssertionError("Unsupported call kind in tower resolver: ${info.callKind}") throw AssertionError("Unsupported call kind in tower resolver: ${info.callKind}")
@@ -5,29 +5,23 @@
package org.jetbrains.kotlin.fir.resolve.calls.tower package org.jetbrains.kotlin.fir.resolve.calls.tower
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.FirConstructor import org.jetbrains.kotlin.fir.declarations.FirConstructor
import org.jetbrains.kotlin.fir.declarations.isInner import org.jetbrains.kotlin.fir.declarations.isInner
import org.jetbrains.kotlin.fir.dispatchReceiverClassOrNull
import org.jetbrains.kotlin.fir.expressions.builder.buildResolvedQualifier import org.jetbrains.kotlin.fir.expressions.builder.buildResolvedQualifier
import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.calls.* import org.jetbrains.kotlin.fir.resolve.calls.*
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType
import org.jetbrains.kotlin.fir.scopes.FirScope import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.scopes.impl.FirDefaultStarImportingScope import org.jetbrains.kotlin.fir.scopes.impl.FirDefaultStarImportingScope
import org.jetbrains.kotlin.fir.scopes.impl.importedFromObjectData import org.jetbrains.kotlin.fir.scopes.impl.importedFromObjectData
import org.jetbrains.kotlin.fir.scopes.processClassifiersByName import org.jetbrains.kotlin.fir.scopes.processClassifiersByName
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.typeContext import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.ConeStarProjection
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.constructClassType
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.AbstractTypeChecker import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.SmartList
enum class ProcessResult { enum class ProcessResult {
FOUND, SCOPE_EMPTY; FOUND, SCOPE_EMPTY;
@@ -46,11 +40,11 @@ abstract class TowerScopeLevel {
object Objects : Token<AbstractFirBasedSymbol<*>>() object Objects : Token<AbstractFirBasedSymbol<*>>()
} }
abstract fun processFunctionsByName(name: Name, processor: TowerScopeLevelProcessor<FirFunctionSymbol<*>>): ProcessResult abstract fun processFunctionsByName(info: CallInfo, processor: TowerScopeLevelProcessor<FirFunctionSymbol<*>>): ProcessResult
abstract fun processPropertiesByName(name: Name, processor: TowerScopeLevelProcessor<FirVariableSymbol<*>>): ProcessResult abstract fun processPropertiesByName(info: CallInfo, processor: TowerScopeLevelProcessor<FirVariableSymbol<*>>): ProcessResult
abstract fun processObjectsByName(name: Name, processor: TowerScopeLevelProcessor<AbstractFirBasedSymbol<*>>): ProcessResult abstract fun processObjectsByName(info: CallInfo, processor: TowerScopeLevelProcessor<AbstractFirBasedSymbol<*>>): ProcessResult
interface TowerScopeLevelProcessor<in T : AbstractFirBasedSymbol<*>> { interface TowerScopeLevelProcessor<in T : AbstractFirBasedSymbol<*>> {
fun consumeCandidate( fun consumeCandidate(
@@ -131,41 +125,50 @@ class MemberScopeTowerLevel(
} }
override fun processFunctionsByName( override fun processFunctionsByName(
name: Name, info: CallInfo,
processor: TowerScopeLevelProcessor<FirFunctionSymbol<*>> processor: TowerScopeLevelProcessor<FirFunctionSymbol<*>>
): ProcessResult { ): ProcessResult {
val isInvoke = name == OperatorNameConventions.INVOKE val isInvoke = info.name == OperatorNameConventions.INVOKE
if (implicitExtensionInvokeMode && !isInvoke) { if (implicitExtensionInvokeMode && !isInvoke) {
return ProcessResult.FOUND return ProcessResult.FOUND
} }
val lookupTracker = session.lookupTracker
return processMembers(processor) { consumer -> return processMembers(processor) { consumer ->
this.processFunctionsAndConstructorsByName( withMemberCallLookup(lookupTracker, info) { lookupCtx ->
name, session, bodyResolveComponents, this.processFunctionsAndConstructorsByName(
includeInnerConstructors = true, info.name, session, bodyResolveComponents,
processor = { includeInnerConstructors = true,
// WARNING, DO NOT CAST FUNCTIONAL TYPE ITSELF processor = {
@Suppress("UNCHECKED_CAST") lookupCtx.recordCallableMemberLookup(it)
consumer(it as FirFunctionSymbol<*>) // WARNING, DO NOT CAST FUNCTIONAL TYPE ITSELF
} @Suppress("UNCHECKED_CAST")
) consumer(it as FirFunctionSymbol<*>)
}
)
}
} }
} }
override fun processPropertiesByName( override fun processPropertiesByName(
name: Name, info: CallInfo,
processor: TowerScopeLevelProcessor<FirVariableSymbol<*>> processor: TowerScopeLevelProcessor<FirVariableSymbol<*>>
): ProcessResult { ): ProcessResult {
val lookupTracker = session.lookupTracker
return processMembers(processor) { consumer -> return processMembers(processor) { consumer ->
this.processPropertiesByName(name) { withMemberCallLookup(lookupTracker, info) { lookupCtx ->
// WARNING, DO NOT CAST FUNCTIONAL TYPE ITSELF lookupTracker?.recordCallLookup(info, dispatchReceiverValue.type)
@Suppress("UNCHECKED_CAST") this.processPropertiesByName(info.name) {
consumer(it) lookupCtx.recordCallableMemberLookup(it)
// WARNING, DO NOT CAST FUNCTIONAL TYPE ITSELF
@Suppress("UNCHECKED_CAST")
consumer(it)
}
} }
} }
} }
override fun processObjectsByName( override fun processObjectsByName(
name: Name, info: CallInfo,
processor: TowerScopeLevelProcessor<AbstractFirBasedSymbol<*>> processor: TowerScopeLevelProcessor<AbstractFirBasedSymbol<*>>
): ProcessResult { ): ProcessResult {
return ProcessResult.FOUND return ProcessResult.FOUND
@@ -176,6 +179,28 @@ class MemberScopeTowerLevel(
session, bodyResolveComponents, receiverValue, extensionReceiver, implicitExtensionInvokeMode, scopeSession session, bodyResolveComponents, receiverValue, extensionReceiver, implicitExtensionInvokeMode, scopeSession
) )
} }
private inline fun withMemberCallLookup(
lookupTracker: FirLookupTrackerComponent?,
info: CallInfo,
body: (Triple<FirLookupTrackerComponent?, SmartList<String>, CallInfo>) -> Unit
) {
lookupTracker?.recordCallLookup(info, dispatchReceiverValue.type)
val lookupScopes = SmartList<String>()
body(Triple(lookupTracker, lookupScopes, info))
if (lookupScopes.isNotEmpty()) {
lookupTracker?.recordCallLookup(info, lookupScopes)
}
}
private fun Triple<FirLookupTrackerComponent?, SmartList<String>, CallInfo>.recordCallableMemberLookup(callable: FirCallableSymbol<*>) {
first?.run {
recordTypeResolveAsLookup(callable.fir.returnTypeRef, third.callSite.source, third.containingFile.source)
callable.callableId.className?.let { lookupScope ->
second.add(lookupScope.asString())
}
}
}
} }
// This is more like "scope-based tower level" // This is more like "scope-based tower level"
@@ -269,12 +294,13 @@ class ScopeTowerLevel(
} }
override fun processFunctionsByName( override fun processFunctionsByName(
name: Name, info: CallInfo,
processor: TowerScopeLevelProcessor<FirFunctionSymbol<*>> processor: TowerScopeLevelProcessor<FirFunctionSymbol<*>>
): ProcessResult { ): ProcessResult {
var empty = true var empty = true
session.lookupTracker?.recordCallLookup(info, scope.scopeOwnerLookupNames)
scope.processFunctionsAndConstructorsByName( scope.processFunctionsAndConstructorsByName(
name, info.name,
session, session,
bodyResolveComponents, bodyResolveComponents,
includeInnerConstructors = includeInnerConstructors includeInnerConstructors = includeInnerConstructors
@@ -286,11 +312,12 @@ class ScopeTowerLevel(
} }
override fun processPropertiesByName( override fun processPropertiesByName(
name: Name, info: CallInfo,
processor: TowerScopeLevelProcessor<FirVariableSymbol<*>> processor: TowerScopeLevelProcessor<FirVariableSymbol<*>>
): ProcessResult { ): ProcessResult {
var empty = true var empty = true
scope.processPropertiesByName(name) { candidate -> session.lookupTracker?.recordCallLookup(info, scope.scopeOwnerLookupNames)
scope.processPropertiesByName(info.name) { candidate ->
empty = false empty = false
consumeCallableCandidate(candidate, processor) consumeCallableCandidate(candidate, processor)
} }
@@ -298,11 +325,12 @@ class ScopeTowerLevel(
} }
override fun processObjectsByName( override fun processObjectsByName(
name: Name, info: CallInfo,
processor: TowerScopeLevelProcessor<AbstractFirBasedSymbol<*>> processor: TowerScopeLevelProcessor<AbstractFirBasedSymbol<*>>
): ProcessResult { ): ProcessResult {
var empty = true var empty = true
scope.processClassifiersByName(name) { session.lookupTracker?.recordCallLookup(info, scope.scopeOwnerLookupNames)
scope.processClassifiersByName(info.name) {
empty = false empty = false
processor.consumeCandidate( processor.consumeCandidate(
it, dispatchReceiverValue = null, it, dispatchReceiverValue = null,
@@ -5,14 +5,13 @@
package org.jetbrains.kotlin.fir.resolve.inference package org.jetbrains.kotlin.fir.resolve.inference
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
import org.jetbrains.kotlin.fir.declarations.builder.buildValueParameter import org.jetbrains.kotlin.fir.declarations.builder.buildValueParameter
import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirResolvable import org.jetbrains.kotlin.fir.expressions.FirResolvable
import org.jetbrains.kotlin.fir.expressions.FirStatement import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.fakeElement
import org.jetbrains.kotlin.fir.resolve.ResolutionMode import org.jetbrains.kotlin.fir.resolve.ResolutionMode
import org.jetbrains.kotlin.fir.resolve.calls.Candidate import org.jetbrains.kotlin.fir.resolve.calls.Candidate
import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate
@@ -26,7 +25,6 @@ import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBod
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirBodyResolveTransformer import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirBodyResolveTransformer
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType
import org.jetbrains.kotlin.fir.resolve.typeFromCallee import org.jetbrains.kotlin.fir.resolve.typeFromCallee
import org.jetbrains.kotlin.fir.resolvedTypeFromPrototype
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
@@ -62,7 +60,9 @@ class FirCallCompleter(
val initialType = components.initialTypeOfCandidate(candidate, call) val initialType = components.initialTypeOfCandidate(candidate, call)
if (call is FirExpression) { if (call is FirExpression) {
call.resultType = typeRef.resolvedTypeFromPrototype(initialType) val resolvedTypeRef = typeRef.resolvedTypeFromPrototype(initialType)
call.resultType = resolvedTypeRef
session.lookupTracker?.recordTypeResolveAsLookup(resolvedTypeRef, call.source, null)
} }
if (expectedTypeRef is FirResolvedTypeRef) { if (expectedTypeRef is FirResolvedTypeRef) {
@@ -208,14 +208,19 @@ class FirCallCompleter(
} }
) )
val lookupTracker = session.lookupTracker
lambdaArgument.valueParameters.forEachIndexed { index, parameter -> lambdaArgument.valueParameters.forEachIndexed { index, parameter ->
parameter.replaceReturnTypeRef( val newReturnTypeRef = parameter.returnTypeRef.resolvedTypeFromPrototype(parameters[index].approximateLambdaInputType())
parameter.returnTypeRef.resolvedTypeFromPrototype(parameters[index].approximateLambdaInputType()) parameter.replaceReturnTypeRef(newReturnTypeRef)
) lookupTracker?.recordTypeResolveAsLookup(newReturnTypeRef, parameter.source, null)
} }
lambdaArgument.replaceValueParameters(lambdaArgument.valueParameters + listOfNotNull(itParam)) lambdaArgument.replaceValueParameters(lambdaArgument.valueParameters + listOfNotNull(itParam))
lambdaArgument.replaceReturnTypeRef(expectedReturnTypeRef ?: components.noExpectedType) lambdaArgument.replaceReturnTypeRef(
expectedReturnTypeRef?.also {
lookupTracker?.recordTypeResolveAsLookup(it, lambdaArgument.source, null)
} ?: components.noExpectedType
)
val builderInferenceSession = runIf(stubsForPostponedVariables.isNotEmpty()) { val builderInferenceSession = runIf(stubsForPostponedVariables.isNotEmpty()) {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
@@ -8,6 +8,8 @@ package org.jetbrains.kotlin.fir.resolve.inference
import org.jetbrains.kotlin.fir.FirCallResolver import org.jetbrains.kotlin.fir.FirCallResolver
import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirStatement import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.lookupTracker
import org.jetbrains.kotlin.fir.recordTypeResolveAsLookup
import org.jetbrains.kotlin.fir.references.builder.buildErrorNamedReference import org.jetbrains.kotlin.fir.references.builder.buildErrorNamedReference
import org.jetbrains.kotlin.fir.resolve.calls.* import org.jetbrains.kotlin.fir.resolve.calls.*
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedReferenceError import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedReferenceError
@@ -93,7 +95,9 @@ class PostponedArgumentsAnalyzer(
namedReference namedReference
).apply { ).apply {
if (resultingCandidate != null) { if (resultingCandidate != null) {
replaceTypeRef(buildResolvedTypeRef { type = resultingCandidate.resultingTypeForCallableReference!! }) val resolvedTypeRef = buildResolvedTypeRef { type = resultingCandidate.resultingTypeForCallableReference!! }
replaceTypeRef(resolvedTypeRef)
resolutionContext.session.lookupTracker?.recordTypeResolveAsLookup(resolvedTypeRef, source, null)
} }
} }
} }
@@ -113,6 +113,7 @@ class FirCallCompletionResultsWriterTransformer(
if (declaration !is FirErrorFunction) { if (declaration !is FirErrorFunction) {
result.replaceTypeArguments(typeArguments) result.replaceTypeArguments(typeArguments)
} }
session.lookupTracker?.recordTypeResolveAsLookup(typeRef, qualifiedAccessExpression.source, null)
return result return result
} }
@@ -135,6 +136,7 @@ class FirCallCompletionResultsWriterTransformer(
val resultType = typeRef.substituteTypeRef(subCandidate) val resultType = typeRef.substituteTypeRef(subCandidate)
resultType.ensureResolvedTypeDeclaration(session) resultType.ensureResolvedTypeDeclaration(session)
result.replaceTypeRef(resultType) result.replaceTypeRef(resultType)
session.lookupTracker?.recordTypeResolveAsLookup(resultType, qualifiedAccessExpression.source, null)
if (mode == Mode.DelegatedPropertyCompletion) { if (mode == Mode.DelegatedPropertyCompletion) {
subCandidate.symbol.fir.transformSingle(declarationWriter, null) subCandidate.symbol.fir.transformSingle(declarationWriter, null)
@@ -179,6 +181,7 @@ class FirCallCompletionResultsWriterTransformer(
} }
result.replaceTypeRef(resultType) result.replaceTypeRef(resultType)
session.lookupTracker?.recordTypeResolveAsLookup(resultType, functionCall.source, null)
if (mode == Mode.DelegatedPropertyCompletion) { if (mode == Mode.DelegatedPropertyCompletion) {
subCandidate.symbol.fir.transformSingle(declarationWriter, null) subCandidate.symbol.fir.transformSingle(declarationWriter, null)
@@ -249,6 +252,7 @@ class FirCallCompletionResultsWriterTransformer(
): D { ): D {
val resultTypeRef = typeRef.substituteTypeRef(calleeReference.candidate) val resultTypeRef = typeRef.substituteTypeRef(calleeReference.candidate)
replaceTypeRef(resultTypeRef) replaceTypeRef(resultTypeRef)
session.lookupTracker?.recordTypeResolveAsLookup(resultTypeRef, source, null)
return this return this
} }
@@ -297,6 +301,7 @@ class FirCallCompletionResultsWriterTransformer(
val resultType = typeRef.withReplacedConeType(finalType) val resultType = typeRef.withReplacedConeType(finalType)
callableReferenceAccess.replaceTypeRef(resultType) callableReferenceAccess.replaceTypeRef(resultType)
callableReferenceAccess.replaceTypeArguments(typeArguments) callableReferenceAccess.replaceTypeArguments(typeArguments)
session.lookupTracker?.recordTypeResolveAsLookup(resultType, typeRef.source ?: callableReferenceAccess.source, null)
return callableReferenceAccess.transformCalleeReference( return callableReferenceAccess.transformCalleeReference(
StoreCalleeReference, StoreCalleeReference,
@@ -338,9 +343,9 @@ class FirCallCompletionResultsWriterTransformer(
): CompositeTransformResult<FirStatement> { ): CompositeTransformResult<FirStatement> {
val originalType = qualifiedAccessExpression.typeRef.coneType val originalType = qualifiedAccessExpression.typeRef.coneType
val substitutedReceiverType = finalSubstitutor.substituteOrNull(originalType) ?: return qualifiedAccessExpression.compose() val substitutedReceiverType = finalSubstitutor.substituteOrNull(originalType) ?: return qualifiedAccessExpression.compose()
qualifiedAccessExpression.replaceTypeRef( val resolvedTypeRef = qualifiedAccessExpression.typeRef.resolvedTypeFromPrototype(substitutedReceiverType)
qualifiedAccessExpression.typeRef.resolvedTypeFromPrototype(substitutedReceiverType) qualifiedAccessExpression.replaceTypeRef(resolvedTypeRef)
) session.lookupTracker?.recordTypeResolveAsLookup(resolvedTypeRef, qualifiedAccessExpression.source, null)
return qualifiedAccessExpression.compose() return qualifiedAccessExpression.compose()
} }
} }
@@ -500,9 +505,9 @@ class FirCallCompletionResultsWriterTransformer(
} }
if (needUpdateLambdaType) { if (needUpdateLambdaType) {
anonymousFunction.replaceTypeRef( val resolvedTypeRef = anonymousFunction.constructFunctionalTypeRef(isSuspend = expectedType?.isSuspendFunctionType(session) == true)
anonymousFunction.constructFunctionalTypeRef(isSuspend = expectedType?.isSuspendFunctionType(session) == true) anonymousFunction.replaceTypeRef(resolvedTypeRef)
) session.lookupTracker?.recordTypeResolveAsLookup(resolvedTypeRef, anonymousFunction.source, null)
} }
val result = transformElement(anonymousFunction, null) val result = transformElement(anonymousFunction, null)
@@ -519,10 +524,14 @@ class FirCallCompletionResultsWriterTransformer(
(returnExpressionsOfAnonymousFunction.lastOrNull() as? FirExpression) (returnExpressionsOfAnonymousFunction.lastOrNull() as? FirExpression)
?.typeRef?.coneTypeSafe<ConeKotlinType>() ?.typeRef?.coneTypeSafe<ConeKotlinType>()
resultFunction.replaceReturnTypeRef(resultFunction.returnTypeRef.withReplacedConeType(lastExpressionType)) val newReturnTypeRef = resultFunction.returnTypeRef.withReplacedConeType(lastExpressionType)
resultFunction.replaceTypeRef( resultFunction.replaceReturnTypeRef(newReturnTypeRef)
resultFunction.constructFunctionalTypeRef(isSuspend = expectedType?.isSuspendFunctionType(session) == true) val resolvedTypeRef = resultFunction.constructFunctionalTypeRef(isSuspend = expectedType?.isSuspendFunctionType(session) == true)
) resultFunction.replaceTypeRef(resolvedTypeRef)
session.lookupTracker?.let {
it.recordTypeResolveAsLookup(newReturnTypeRef, anonymousFunction.source, null)
it.recordTypeResolveAsLookup(resolvedTypeRef, anonymousFunction.source, null)
}
} }
return result return result
@@ -576,6 +585,7 @@ class FirCallCompletionResultsWriterTransformer(
resultType = resultType.resolvedTypeFromPrototype(it.getApproximatedType(data?.getExpectedType(block))) resultType = resultType.resolvedTypeFromPrototype(it.getApproximatedType(data?.getExpectedType(block)))
} }
block.replaceTypeRef(resultType) block.replaceTypeRef(resultType)
session.lookupTracker?.recordTypeResolveAsLookup(resultType, block.source, null)
} }
transformElement(block, data) transformElement(block, data)
if (block.resultType is FirErrorTypeRef) { if (block.resultType is FirErrorTypeRef) {
@@ -11,8 +11,9 @@ import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirImport import org.jetbrains.kotlin.fir.declarations.FirImport
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.declarations.builder.buildResolvedImport import org.jetbrains.kotlin.fir.declarations.builder.buildResolvedImport
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider import org.jetbrains.kotlin.fir.lookupTracker
import org.jetbrains.kotlin.fir.resolve.ScopeSession import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.symbolProvider import org.jetbrains.kotlin.fir.resolve.symbolProvider
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.visitors.CompositeTransformResult import org.jetbrains.kotlin.fir.visitors.CompositeTransformResult
@@ -36,10 +37,20 @@ open class FirImportResolveTransformer protected constructor(
private val symbolProvider: FirSymbolProvider = session.symbolProvider private val symbolProvider: FirSymbolProvider = session.symbolProvider
private var currentFile: FirFile? = null
override fun transformFile(file: FirFile, data: Nothing?): CompositeTransformResult<FirFile> { override fun transformFile(file: FirFile, data: Nothing?): CompositeTransformResult<FirFile> {
checkSessionConsistency(file) checkSessionConsistency(file)
file.replaceResolvePhase(transformerPhase) file.replaceResolvePhase(transformerPhase)
return file.also { it.transformChildren(this, null) }.compose() return file.also {
val prevValue = currentFile
currentFile = file
try {
it.transformChildren(this, null)
} finally {
currentFile = prevValue
}
}.compose()
} }
override fun transformImport(import: FirImport, data: Nothing?): CompositeTransformResult<FirImport> { override fun transformImport(import: FirImport, data: Nothing?): CompositeTransformResult<FirImport> {
@@ -52,6 +63,9 @@ open class FirImportResolveTransformer protected constructor(
} }
val parentFqName = fqName.parent() val parentFqName = fqName.parent()
currentFile?.let {
session.lookupTracker?.recordLookup(fqName.shortName(), parentFqName.asString(), import.source, it.source)
}
return transformImportForFqName(parentFqName, import) return transformImportForFqName(parentFqName, import)
} }
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.fir.resolve.transformers package org.jetbrains.kotlin.fir.resolve.transformers
import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
import org.jetbrains.kotlin.fir.diagnostics.ConeUnexpectedTypeArgumentsError import org.jetbrains.kotlin.fir.diagnostics.ConeUnexpectedTypeArgumentsError
@@ -38,16 +39,35 @@ class FirSpecificTypeResolverTransformer(
} }
} }
@PrivateForInline
@JvmField
var currentFile: FirFile? = null
@OptIn(PrivateForInline::class)
inline fun <R> withFile(file: FirFile?, block: FirSpecificTypeResolverTransformer.() -> R): R {
val oldValue = currentFile
currentFile = file
return try {
block()
} finally {
currentFile = oldValue
}
}
@OptIn(PrivateForInline::class)
override fun transformTypeRef(typeRef: FirTypeRef, data: FirScope): CompositeTransformResult<FirResolvedTypeRef> { override fun transformTypeRef(typeRef: FirTypeRef, data: FirScope): CompositeTransformResult<FirResolvedTypeRef> {
session.lookupTracker?.recordTypeLookup(typeRef, data.scopeOwnerLookupNames, currentFile?.source)
typeRef.transformChildren(this, data) typeRef.transformChildren(this, data)
return transformType(typeRef, typeResolver.resolveType(typeRef, data, areBareTypesAllowed)) return transformType(typeRef, typeResolver.resolveType(typeRef, data, areBareTypesAllowed))
} }
@OptIn(PrivateForInline::class)
override fun transformFunctionTypeRef( override fun transformFunctionTypeRef(
functionTypeRef: FirFunctionTypeRef, functionTypeRef: FirFunctionTypeRef,
data: FirScope data: FirScope
): CompositeTransformResult<FirResolvedTypeRef> { ): CompositeTransformResult<FirResolvedTypeRef> {
functionTypeRef.transformChildren(this, data) functionTypeRef.transformChildren(this, data)
session.lookupTracker?.recordTypeLookup(functionTypeRef, data.scopeOwnerLookupNames, currentFile?.source)
val resolvedType = typeResolver.resolveType(functionTypeRef, data, areBareTypesAllowed).takeIfAcceptable() val resolvedType = typeResolver.resolveType(functionTypeRef, data, areBareTypesAllowed).takeIfAcceptable()
return if (resolvedType != null && resolvedType !is ConeClassErrorType) { return if (resolvedType != null && resolvedType !is ConeClassErrorType) {
buildResolvedTypeRef { buildResolvedTypeRef {
@@ -8,16 +8,15 @@ package org.jetbrains.kotlin.fir.resolve.transformers
import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList import kotlinx.collections.immutable.toPersistentList
import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
import org.jetbrains.kotlin.fir.expressions.FirStatement import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.extensions.extensionService import org.jetbrains.kotlin.fir.extensions.extensionService
import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider
import org.jetbrains.kotlin.fir.extensions.supertypeGenerators import org.jetbrains.kotlin.fir.extensions.supertypeGenerators
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.isLocalClassOrAnonymousObject
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeTypeParameterSupertype import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeTypeParameterSupertype
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.LocalClassesNavigationInfo import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.LocalClassesNavigationInfo
import org.jetbrains.kotlin.fir.scopes.FirCompositeScope import org.jetbrains.kotlin.fir.scopes.FirCompositeScope
@@ -284,6 +283,14 @@ private class FirSupertypeResolverVisitor(
supertypeRefs: List<FirTypeRef> supertypeRefs: List<FirTypeRef>
): List<FirTypeRef> { ): List<FirTypeRef> {
return resolveSpecificClassLikeSupertypes(classLikeDeclaration) { transformer, scope -> return resolveSpecificClassLikeSupertypes(classLikeDeclaration) { transformer, scope ->
if (!classLikeDeclaration.isLocalClassOrAnonymousObject()) {
session.lookupTracker?.let {
val fileSource = session.firProvider.getFirClassifierContainerFile(classLikeDeclaration.symbol).source
for (supertypeRef in supertypeRefs) {
it.recordTypeLookup(supertypeRef, scope.scopeOwnerLookupNames, fileSource)
}
}
}
/* /*
This list is backed by mutable list and during iterating on it we can resolve supertypes of that class via IDE light classes This list is backed by mutable list and during iterating on it we can resolve supertypes of that class via IDE light classes
as IJ Java resolve may resolve a lot of stuff by light classes as IJ Java resolve may resolve a lot of stuff by light classes
@@ -62,6 +62,7 @@ class FirSyntheticCallGenerator(
arguments += whenExpression.branches.map { it.result } arguments += whenExpression.branches.map { it.result }
} }
val reference = generateCalleeReferenceWithCandidate( val reference = generateCalleeReferenceWithCandidate(
whenExpression,
whenSelectFunction, whenSelectFunction,
argumentList, argumentList,
SyntheticCallableId.WHEN.callableName, SyntheticCallableId.WHEN.callableName,
@@ -85,6 +86,7 @@ class FirSyntheticCallGenerator(
} }
val reference = generateCalleeReferenceWithCandidate( val reference = generateCalleeReferenceWithCandidate(
tryExpression,
trySelectFunction, trySelectFunction,
argumentList, argumentList,
SyntheticCallableId.TRY.callableName, SyntheticCallableId.TRY.callableName,
@@ -99,6 +101,7 @@ class FirSyntheticCallGenerator(
if (stubReference !is FirStubReference) return null if (stubReference !is FirStubReference) return null
val reference = generateCalleeReferenceWithCandidate( val reference = generateCalleeReferenceWithCandidate(
checkNotNullCall,
checkNotNullFunction, checkNotNullFunction,
checkNotNullCall.argumentList, checkNotNullCall.argumentList,
SyntheticCallableId.CHECK_NOT_NULL.callableName, SyntheticCallableId.CHECK_NOT_NULL.callableName,
@@ -116,6 +119,7 @@ class FirSyntheticCallGenerator(
arguments += elvisExpression.rhs arguments += elvisExpression.rhs
} }
val reference = generateCalleeReferenceWithCandidate( val reference = generateCalleeReferenceWithCandidate(
elvisExpression,
elvisFunction, elvisFunction,
argumentList, argumentList,
SyntheticCallableId.ELVIS_NOT_NULL.callableName, SyntheticCallableId.ELVIS_NOT_NULL.callableName,
@@ -134,6 +138,7 @@ class FirSyntheticCallGenerator(
val reference = val reference =
generateCalleeReferenceWithCandidate( generateCalleeReferenceWithCandidate(
callableReferenceAccess,
idFunction, idFunction,
argumentList, argumentList,
SyntheticCallableId.ID.callableName, SyntheticCallableId.ID.callableName,
@@ -156,13 +161,14 @@ class FirSyntheticCallGenerator(
} }
private fun generateCalleeReferenceWithCandidate( private fun generateCalleeReferenceWithCandidate(
callSite: FirExpression,
function: FirSimpleFunction, function: FirSimpleFunction,
argumentList: FirArgumentList, argumentList: FirArgumentList,
name: Name, name: Name,
callKind: CallKind = CallKind.SyntheticSelect, callKind: CallKind = CallKind.SyntheticSelect,
context: ResolutionContext context: ResolutionContext
): FirNamedReferenceWithCandidate? { ): FirNamedReferenceWithCandidate? {
val callInfo = generateCallInfo(name, argumentList, callKind) val callInfo = generateCallInfo(callSite, name, argumentList, callKind)
val candidate = generateCandidate(callInfo, function, context) val candidate = generateCandidate(callInfo, function, context)
val applicability = components.resolutionStageRunner.processCandidate(candidate, context) val applicability = components.resolutionStageRunner.processCandidate(candidate, context)
if (applicability <= CandidateApplicability.INAPPLICABLE) { if (applicability <= CandidateApplicability.INAPPLICABLE) {
@@ -182,7 +188,8 @@ class FirSyntheticCallGenerator(
) )
} }
private fun generateCallInfo(name: Name, argumentList: FirArgumentList, callKind: CallKind) = CallInfo( private fun generateCallInfo(callSite: FirExpression, name: Name, argumentList: FirArgumentList, callKind: CallKind) = CallInfo(
callSite = callSite,
callKind = callKind, callKind = callKind,
name = name, name = name,
explicitReceiver = null, explicitReceiver = null,
@@ -50,9 +50,11 @@ class FirTypeResolveTransformer(
} }
private val typeResolverTransformer: FirSpecificTypeResolverTransformer = FirSpecificTypeResolverTransformer(session) private val typeResolverTransformer: FirSpecificTypeResolverTransformer = FirSpecificTypeResolverTransformer(session)
private var currentFile: FirFile? = null
override fun transformFile(file: FirFile, data: Nothing?): CompositeTransformResult<FirFile> { override fun transformFile(file: FirFile, data: Nothing?): CompositeTransformResult<FirFile> {
checkSessionConsistency(file) checkSessionConsistency(file)
currentFile = file
return withScopeCleanup { return withScopeCleanup {
scopes.addAll(createImportingScopes(file, session, scopeSession)) scopes.addAll(createImportingScopes(file, session, scopeSession))
super.transformFile(file, data) super.transformFile(file, data)
@@ -169,7 +171,7 @@ class FirTypeResolveTransformer(
} }
override fun transformTypeRef(typeRef: FirTypeRef, data: Nothing?): CompositeTransformResult<FirResolvedTypeRef> { override fun transformTypeRef(typeRef: FirTypeRef, data: Nothing?): CompositeTransformResult<FirResolvedTypeRef> {
return typeRef.transform(typeResolverTransformer, towerScope) return typeResolverTransformer.withFile(currentFile) { typeRef.transform(typeResolverTransformer, towerScope) }
} }
override fun transformValueParameter(valueParameter: FirValueParameter, data: Nothing?): CompositeTransformResult<FirStatement> { override fun transformValueParameter(valueParameter: FirValueParameter, data: Nothing?): CompositeTransformResult<FirStatement> {
@@ -66,7 +66,9 @@ open class FirBodyResolveTransformer(
if (typeRef is FirResolvedTypeRef) { if (typeRef is FirResolvedTypeRef) {
return typeRef.compose() return typeRef.compose()
} }
return typeResolverTransformer.transformTypeRef(typeRef, FirCompositeScope(components.createCurrentScopeList())) return typeResolverTransformer.withFile(context.file) {
transformTypeRef(typeRef, FirCompositeScope(components.createCurrentScopeList()))
}
} }
override fun transformImplicitTypeRef(implicitTypeRef: FirImplicitTypeRef, data: ResolutionMode): CompositeTransformResult<FirTypeRef> { override fun transformImplicitTypeRef(implicitTypeRef: FirImplicitTypeRef, data: ResolutionMode): CompositeTransformResult<FirTypeRef> {
@@ -201,6 +201,8 @@ open class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransfor
}.toTypedArray(), }.toTypedArray(),
isNullable = false isNullable = false
) )
}.also {
session.lookupTracker?.recordTypeResolveAsLookup(it, propertyReferenceAccess.source ?: source, null)
} }
) )
} }
@@ -716,11 +718,17 @@ open class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransfor
returnStatements.mapNotNull { (it as? FirExpression)?.resultType?.coneType } returnStatements.mapNotNull { (it as? FirExpression)?.resultType?.coneType }
) ?: session.builtinTypes.unitType.type ) ?: session.builtinTypes.unitType.type
} }
lambda.replaceReturnTypeRef(lambda.returnTypeRef.resolvedTypeFromPrototype(returnType)) lambda.replaceReturnTypeRef(
lambda.returnTypeRef.resolvedTypeFromPrototype(returnType).also {
session.lookupTracker?.recordTypeResolveAsLookup(it, lambda.source, null)
}
)
lambda.replaceTypeRef( lambda.replaceTypeRef(
lambda.constructFunctionalTypeRef( lambda.constructFunctionalTypeRef(
isSuspend = expectedTypeRef.coneTypeSafe<ConeKotlinType>()?.isSuspendFunctionType(session) == true isSuspend = expectedTypeRef.coneTypeSafe<ConeKotlinType>()?.isSuspendFunctionType(session) == true
) ).also {
session.lookupTracker?.recordTypeResolveAsLookup(it, lambda.source, null)
}
) )
lambda.addReturn().compose() lambda.addReturn().compose()
} }
@@ -718,7 +718,11 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
} }
val typeRef = symbol?.constructType(typeArguments, isNullable = false) val typeRef = symbol?.constructType(typeArguments, isNullable = false)
if (typeRef != null) { if (typeRef != null) {
lhs.replaceTypeRef(buildResolvedTypeRef { type = typeRef }) lhs.replaceTypeRef(
buildResolvedTypeRef { type = typeRef }.also {
session.lookupTracker?.recordTypeResolveAsLookup(it, getClassCall.source, null)
}
)
typeRef typeRef
} else { } else {
lhs.resultType.coneType lhs.resultType.coneType
@@ -219,13 +219,18 @@ private class ReturnTypeCalculatorWithJump(
if (declaration.isIntersectionOverride) { if (declaration.isIntersectionOverride) {
val result = tryCalculateReturnType(declaration.symbol.baseForIntersectionOverride!!.fir) val result = tryCalculateReturnType(declaration.symbol.baseForIntersectionOverride!!.fir)
declaration.replaceReturnTypeRef(result) declaration.replaceReturnTypeRef(result)
session.lookupTracker?.recordTypeResolveAsLookup(result, declaration.source, null)
return result return result
} }
runIf(declaration.isSubstitutionOverride) { runIf(declaration.isSubstitutionOverride) {
val overriddenDeclaration = declaration.originalForSubstitutionOverride ?: return@runIf val overriddenDeclaration = declaration.originalForSubstitutionOverride ?: return@runIf
tryCalculateReturnType(overriddenDeclaration) tryCalculateReturnType(overriddenDeclaration)
return FakeOverrideTypeCalculator.Forced.computeReturnType(declaration) val result = FakeOverrideTypeCalculator.Forced.computeReturnType(declaration)
(declaration.returnTypeRef as? FirResolvedTypeRef)?.let {
session.lookupTracker?.recordTypeResolveAsLookup(it, declaration.source, null)
}
return result
} }
return when (val status = implicitBodyResolveComputationSession.getStatus(declaration.symbol)) { return when (val status = implicitBodyResolveComputationSession.getStatus(declaration.symbol)) {
@@ -19,4 +19,8 @@ class FirExplicitSimpleImportingScope(
imports.filterIsInstance<FirResolvedImport>() imports.filterIsInstance<FirResolvedImport>()
.filter { !it.isAllUnder && it.importedName != null } .filter { !it.isAllUnder && it.importedName != null }
.groupBy { it.aliasName ?: it.importedName!! } .groupBy { it.aliasName ?: it.importedName!! }
override val scopeOwnerLookupNames: List<String> by lazy(LazyThreadSafetyMode.PUBLICATION) {
simpleImports.values.flatMapTo(LinkedHashSet()) { it.map { it.packageFqName.asString() } }.toList()
}
} }
@@ -17,4 +17,8 @@ class FirExplicitStarImportingScope(
filter: FirImportingScopeFilter filter: FirImportingScopeFilter
) : FirAbstractStarImportingScope(session, scopeSession, filter, lookupInFir = true) { ) : FirAbstractStarImportingScope(session, scopeSession, filter, lookupInFir = true) {
override val starImports = imports.filterIsInstance<FirResolvedImport>().filter { it.isAllUnder } override val starImports = imports.filterIsInstance<FirResolvedImport>().filter { it.isAllUnder }
override val scopeOwnerLookupNames: List<String> by lazy(LazyThreadSafetyMode.PUBLICATION) {
starImports.mapTo(LinkedHashSet()) { it.packageFqName.asString() }.toList()
}
} }
@@ -43,6 +43,9 @@ private class FirNestedClassifierScopeWithSubstitution(
override fun getCallableNames(): Set<Name> = scope.getContainingCallableNamesIfPresent() override fun getCallableNames(): Set<Name> = scope.getContainingCallableNamesIfPresent()
override fun getClassifierNames(): Set<Name> = scope.getContainingClassifierNamesIfPresent() override fun getClassifierNames(): Set<Name> = scope.getContainingClassifierNamesIfPresent()
override val scopeOwnerLookupNames: List<String>
get() = scope.scopeOwnerLookupNames
} }
fun FirScope.wrapNestedClassifierScopeWithSubstitutionForSuperType( fun FirScope.wrapNestedClassifierScopeWithSubstitutionForSuperType(
@@ -18,4 +18,7 @@ class FirOnlyCallablesScope(val delegate: FirScope) : FirScope() {
override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) { override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) {
return delegate.processPropertiesByName(name, processor) return delegate.processPropertiesByName(name, processor)
} }
override val scopeOwnerLookupNames: List<String>
get() = delegate.scopeOwnerLookupNames
} }
@@ -10,10 +10,14 @@ import org.jetbrains.kotlin.fir.resolve.symbolProvider
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.transformers.ensureResolvedForCalls import org.jetbrains.kotlin.fir.resolve.transformers.ensureResolvedForCalls
import org.jetbrains.kotlin.fir.scopes.FirScope import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.symbols.impl.FirClassifierSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.SmartList
class FirPackageMemberScope(val fqName: FqName, val session: FirSession) : FirScope() { class FirPackageMemberScope(val fqName: FqName, val session: FirSession) : FirScope() {
private val symbolProvider = session.symbolProvider private val symbolProvider = session.symbolProvider
@@ -56,4 +60,6 @@ class FirPackageMemberScope(val fqName: FqName, val session: FirSession) : FirSc
processor(symbol) processor(symbol)
} }
} }
override val scopeOwnerLookupNames: List<String> = SmartList(fqName.asString())
} }
@@ -80,4 +80,7 @@ class FirScopeWithFakeOverrideTypeCalculator(
fakeOverrideTypeCalculator.computeReturnType(declaration) fakeOverrideTypeCalculator.computeReturnType(declaration)
} }
} }
override val scopeOwnerLookupNames: List<String>
get() = delegate.scopeOwnerLookupNames
} }
@@ -52,4 +52,8 @@ class FirCompositeScope(val scopes: Iterable<FirScope>) : FirScope(), FirContain
override fun getClassifierNames(): Set<Name> { override fun getClassifierNames(): Set<Name> {
return scopes.flatMapTo(hashSetOf()) { it.getContainingClassifierNamesIfPresent() } return scopes.flatMapTo(hashSetOf()) { it.getContainingClassifierNamesIfPresent() }
} }
override val scopeOwnerLookupNames: List<String> by lazy(LazyThreadSafetyMode.PUBLICATION) {
scopes.flatMap { it.scopeOwnerLookupNames }
}
} }
@@ -34,6 +34,8 @@ abstract class FirScope {
} }
open fun mayContainName(name: Name) = true open fun mayContainName(name: Name) = true
open val scopeOwnerLookupNames: List<String> get() = emptyList()
} }
fun FirScope.getSingleClassifier(name: Name): FirClassifierSymbol<*>? = mutableListOf<FirClassifierSymbol<*>>().apply { fun FirScope.getSingleClassifier(name: Name): FirClassifierSymbol<*>? = mutableListOf<FirClassifierSymbol<*>>().apply {
@@ -9,6 +9,7 @@ import com.intellij.openapi.project.Project
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.load.kotlin.PackagePartProvider import org.jetbrains.kotlin.load.kotlin.PackagePartProvider
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
@@ -17,13 +18,15 @@ fun createSessionForTests(
sourceScope: GlobalSearchScope, sourceScope: GlobalSearchScope,
librariesScope: GlobalSearchScope = GlobalSearchScope.notScope(sourceScope), librariesScope: GlobalSearchScope = GlobalSearchScope.notScope(sourceScope),
moduleName: String = "TestModule", moduleName: String = "TestModule",
friendPaths: List<String> = emptyList() friendPaths: List<String> = emptyList(),
lookupTracker: LookupTracker? = null
): FirSession = createSessionForTests( ): FirSession = createSessionForTests(
environment.project, environment.project,
sourceScope, sourceScope,
librariesScope, librariesScope,
moduleName, moduleName,
friendPaths, friendPaths,
lookupTracker,
environment::createPackagePartProvider environment::createPackagePartProvider
) )
@@ -33,6 +36,7 @@ fun createSessionForTests(
librariesScope: GlobalSearchScope, librariesScope: GlobalSearchScope,
moduleName: String = "TestModule", moduleName: String = "TestModule",
friendPaths: List<String> = emptyList(), friendPaths: List<String> = emptyList(),
lookupTracker: LookupTracker? = null,
packagePartProvider: (GlobalSearchScope) -> PackagePartProvider packagePartProvider: (GlobalSearchScope) -> PackagePartProvider
): FirSession { ): FirSession {
return createSessionWithDependencies( return createSessionWithDependencies(
@@ -43,6 +47,7 @@ fun createSessionForTests(
languageVersionSettings = LanguageVersionSettingsImpl.DEFAULT, languageVersionSettings = LanguageVersionSettingsImpl.DEFAULT,
sourceScope, sourceScope,
librariesScope, librariesScope,
lookupTracker,
packagePartProvider packagePartProvider
) )
} }
@@ -36,6 +36,7 @@ abstract class AbstractCandidateInfoProvider(
) : CandidateInfoProvider { ) : CandidateInfoProvider {
override fun callInfo(): CallInfo = with(resolutionParameters) { override fun callInfo(): CallInfo = with(resolutionParameters) {
CallInfo( CallInfo(
firFile, // TODO: consider passing more precise info here, if needed
callKind = callKind(), callKind = callKind(),
name = callableSymbol.callableId.callableName, name = callableSymbol.callableId.callableName,
explicitReceiver = explicitReceiver, explicitReceiver = explicitReceiver,