FIR: Rework qualifier-in-expression resolution

Get rid of stateful FirQualifiedNameResolver
Resolve a.b.c-like calls step-by-step using just recursion

^KT-48363 Fixed
This commit is contained in:
Denis.Zharkov
2021-09-07 13:09:05 +03:00
committed by teamcityserver
parent 69f7c2278d
commit 9575d4d242
17 changed files with 341 additions and 182 deletions
@@ -22641,6 +22641,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
runTest("compiler/testData/diagnostics/tests/qualifiedExpression/TypeWithError.kt");
}
@Test
@TestMetadata("variableVsImportedClassifier.kt")
public void testVariableVsImportedClassifier() throws Exception {
runTest("compiler/testData/diagnostics/tests/qualifiedExpression/variableVsImportedClassifier.kt");
}
@Test
@TestMetadata("visibleClassVsQualifiedClass.kt")
public void testVisibleClassVsQualifiedClass() throws Exception {
@@ -22641,6 +22641,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
runTest("compiler/testData/diagnostics/tests/qualifiedExpression/TypeWithError.kt");
}
@Test
@TestMetadata("variableVsImportedClassifier.kt")
public void testVariableVsImportedClassifier() throws Exception {
runTest("compiler/testData/diagnostics/tests/qualifiedExpression/variableVsImportedClassifier.kt");
}
@Test
@TestMetadata("visibleClassVsQualifiedClass.kt")
public void testVisibleClassVsQualifiedClass() throws Exception {
@@ -49,7 +49,6 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class FirCallResolver(
private val components: FirAbstractBodyResolveTransformer.BodyResolveTransformerComponents,
private val qualifiedResolver: FirQualifiedNameResolver,
) {
private val session = components.session
private val overloadByLambdaReturnTypeResolver = FirOverloadByLambdaReturnTypeResolver(components)
@@ -72,7 +71,6 @@ class FirCallResolver(
@OptIn(PrivateForInline::class)
fun resolveCallAndSelectCandidate(functionCall: FirFunctionCall): FirFunctionCall {
qualifiedResolver.reset()
@Suppress("NAME_SHADOWING")
val functionCall = if (needTransformArguments) {
components.context.withIncrementedQualifierPartIndex { functionCall.transformExplicitReceiver() }
@@ -138,13 +136,22 @@ class FirCallResolver(
explicitReceiver as? FirQualifiedAccessExpression
?: return transformExplicitReceiver(transformer, ResolutionMode.ContextIndependent) as Q
val callee =
explicitReceiver.calleeReference as? FirSuperReference
?: return transformExplicitReceiver(transformer, ResolutionMode.ContextIndependent) as Q
(explicitReceiver.calleeReference as? FirSuperReference)?.let {
transformer.transformSuperReceiver(it, explicitReceiver, this)
return this
}
transformer.transformSuperReceiver(callee, explicitReceiver, this)
if (explicitReceiver is FirPropertyAccessExpression) {
this.replaceExplicitReceiver(
transformer.transformQualifiedAccessExpression(
explicitReceiver, ResolutionMode.ContextIndependent,
isUsedAsReceiver = true
) as FirExpression
)
return this
}
return this
return transformExplicitReceiver(transformer, ResolutionMode.ContextIndependent) as Q
}
private data class ResolutionResult(
@@ -202,12 +209,15 @@ class FirCallResolver(
return ResolutionResult(info, result.currentApplicability, reducedCandidates)
}
fun <T : FirQualifiedAccess> resolveVariableAccessAndSelectCandidate(qualifiedAccess: T): FirStatement {
return resolveVariableAccessAndSelectCandidateImpl(qualifiedAccess) { true }
fun <T : FirQualifiedAccess> resolveVariableAccessAndSelectCandidate(qualifiedAccess: T, isUsedAsReceiver: Boolean): FirStatement {
return resolveVariableAccessAndSelectCandidateImpl(qualifiedAccess, isUsedAsReceiver) { true }
}
fun resolveOnlyEnumOrQualifierAccessAndSelectCandidate(qualifiedAccess: FirQualifiedAccessExpression): FirStatement {
return resolveVariableAccessAndSelectCandidateImpl(qualifiedAccess) accept@{ candidates ->
fun resolveOnlyEnumOrQualifierAccessAndSelectCandidate(
qualifiedAccess: FirQualifiedAccessExpression,
isUsedAsReceiver: Boolean,
): FirStatement {
return resolveVariableAccessAndSelectCandidateImpl(qualifiedAccess, isUsedAsReceiver) accept@{ candidates ->
val symbol = candidates.singleOrNull()?.symbol ?: return@accept false
symbol is FirEnumEntrySymbol || symbol is FirRegularClassSymbol
}
@@ -215,30 +225,48 @@ class FirCallResolver(
private fun <T : FirQualifiedAccess> resolveVariableAccessAndSelectCandidateImpl(
qualifiedAccess: T,
isUsedAsReceiver: Boolean,
acceptCandidates: (Collection<Candidate>) -> Boolean
): FirStatement {
val callee = qualifiedAccess.calleeReference as? FirSimpleNamedReference ?: return qualifiedAccess
return components.context.withIncrementedQualifierPartIndex {
qualifiedResolver.initProcessingQualifiedAccess(callee, qualifiedAccess.typeArguments)
@Suppress("NAME_SHADOWING")
val qualifiedAccess = qualifiedAccess.transformExplicitReceiver<FirQualifiedAccess>()
qualifiedResolver.replacedQualifier(qualifiedAccess)?.let { resolvedQualifierPart ->
return@withIncrementedQualifierPartIndex resolvedQualifierPart
val nonFatalDiagnosticFromExpression = (qualifiedAccess as? FirPropertyAccessExpression)?.nonFatalDiagnostics
if (isUsedAsReceiver) {
(qualifiedAccess.explicitReceiver as? FirResolvedQualifier)
?.continueQualifier(
callee,
qualifiedAccess.source,
qualifiedAccess.typeArguments,
nonFatalDiagnosticFromExpression,
components
)
?.let { return it }
}
var result = collectCandidates(qualifiedAccess, callee.name)
if (qualifiedAccess.explicitReceiver == null) {
if (!result.applicability.isSuccess) {
// We should run QualifierResolver if no successful candidates are available
// Otherwise expression (even ambiguous) beat qualifier
qualifiedResolver.tryResolveAsQualifier(qualifiedAccess.source)?.let { resolvedQualifier ->
return@withIncrementedQualifierPartIndex resolvedQualifier
}
// Even if we successfully resolved to some companion/named object, we should re-try with qualifier resolution
// import D.*
// class A {
// object B
// }
// class D {
// object A
// }
// fun main() {
// A // should resolved to D.A
// A.B // should be resolved to A.B
// }
if (!result.applicability.isSuccess || (isUsedAsReceiver && result.candidates.all { it.symbol is FirRegularClassSymbol })) {
components.resolveRootPartOfQualifier(
callee, qualifiedAccess.source, qualifiedAccess.typeArguments, nonFatalDiagnosticFromExpression,
)?.let { return it }
}
qualifiedResolver.reset()
}
var functionCallExpected = false
@@ -283,14 +311,14 @@ class FirCallResolver(
referencedSymbol is FirClassLikeSymbol<*> -> {
return@withIncrementedQualifierPartIndex components.buildResolvedQualifierForClass(
referencedSymbol,
nameReference.source,
qualifiedAccess.source,
qualifiedAccess.typeArguments,
diagnostic,
nonFatalDiagnostics = extractNonFatalDiagnostics(
nameReference.source,
qualifiedAccess.explicitReceiver,
referencedSymbol,
(qualifiedAccess as? FirPropertyAccessExpression)?.nonFatalDiagnostics
nonFatalDiagnosticFromExpression,
)
)
}
@@ -314,32 +342,6 @@ class FirCallResolver(
}
}
private fun extractNonFatalDiagnostics(
source: FirSourceElement?,
explicitReceiver: FirExpression?,
symbol: FirClassLikeSymbol<*>,
extraNotFatalDiagnostics: List<ConeDiagnostic>?
): List<ConeDiagnostic> {
val prevDiagnostics = (explicitReceiver as? FirResolvedQualifier)?.nonFatalDiagnostics ?: emptyList()
var result: MutableList<ConeDiagnostic>? = null
val deprecation = symbol.fir.deprecation?.forUseSite()
if (deprecation != null) {
result = mutableListOf()
result.addAll(prevDiagnostics)
result.add(ConeDeprecated(source, symbol, deprecation))
}
if (extraNotFatalDiagnostics != null && extraNotFatalDiagnostics.isNotEmpty()) {
if (result == null) {
result = mutableListOf()
result.addAll(prevDiagnostics)
}
result.addAll(extraNotFatalDiagnostics)
}
return result?.toList() ?: prevDiagnostics
}
fun resolveCallableReference(
constraintSystemBuilder: ConstraintSystemBuilder,
resolvedCallableReferenceAtom: ResolvedCallableReferenceAtom,
@@ -1,109 +0,0 @@
/*
* Copyright 2010-2019 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.expressions.FirQualifiedAccess
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.expressions.builder.buildResolvedQualifier
import org.jetbrains.kotlin.fir.references.impl.FirSimpleNamedReference
import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents
import org.jetbrains.kotlin.fir.resolve.symbolProvider
import org.jetbrains.kotlin.fir.resolve.transformers.PackageOrClass
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType
import org.jetbrains.kotlin.fir.resolve.transformers.resolveToPackageOrClass
import org.jetbrains.kotlin.fir.resolve.typeForQualifier
import org.jetbrains.kotlin.fir.types.FirTypeProjection
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
const val ROOT_PREFIX_FOR_IDE_RESOLUTION_MODE = "_root_ide_package_"
class FirQualifiedNameResolver(private val components: BodyResolveComponents) {
private val session = components.session
private var qualifierStack = mutableListOf<NameWithTypeArguments>()
private var qualifierPartsToDrop = 0
private class NameWithTypeArguments(val name: Name, val typeArguments: List<FirTypeProjection>)
fun reset() {
qualifierStack.clear()
qualifierPartsToDrop = 0
}
/**
* NB: 0 if current 'qualifiedAccess.safe || callee.name.isSpecial', 1 if current is fine, 2 if potential qualifier
* a.b.c
* ^ here stack will be ['c', 'b'], so possible
* a.b?.c
* ^ here stack will be ['b'], so impossible
* a?.b.c
* ^ here stack will be [], so impossible
*/
fun isPotentialQualifierPartPosition() = qualifierStack.size > 1
fun initProcessingQualifiedAccess(callee: FirSimpleNamedReference, typeArguments: List<FirTypeProjection>) {
if (callee.name.isSpecial) {
qualifierStack.clear()
} else {
qualifierStack.add(NameWithTypeArguments(callee.name, typeArguments.toList()))
}
}
fun replacedQualifier(qualifiedAccess: FirQualifiedAccess): FirStatement? =
if (qualifierPartsToDrop > 0) {
qualifierPartsToDrop--
if (qualifierPartsToDrop == 0) {
reset()
}
qualifiedAccess.explicitReceiver ?: qualifiedAccess
} else {
null
}
fun tryResolveAsQualifier(source: FirSourceElement?): FirResolvedQualifier? {
if (qualifierStack.isEmpty()) {
return null
}
val symbolProvider = session.symbolProvider
var qualifierParts = qualifierStack.asReversed().map { it.name.asString() }
val fakeRootIdePrefixIsPresent = qualifierParts.firstOrNull() == ROOT_PREFIX_FOR_IDE_RESOLUTION_MODE
if (fakeRootIdePrefixIsPresent) {
qualifierParts = qualifierParts.drop(1)
}
var resolved: PackageOrClass?
do {
resolved = resolveToPackageOrClass(
symbolProvider,
FqName.fromSegments(qualifierParts)
)
if (resolved == null)
qualifierParts = qualifierParts.dropLast(1)
} while (resolved == null && qualifierParts.isNotEmpty())
if (resolved != null) {
qualifierPartsToDrop = qualifierParts.size - 1
// we would need to drop fake package too
if (fakeRootIdePrefixIsPresent) {
qualifierPartsToDrop += 1
}
return buildResolvedQualifier {
this.source = source?.getWholeQualifierSourceIfPossible(qualifierPartsToDrop)
packageFqName = resolved.packageFqName
relativeClassFqName = resolved.relativeClassFqName
symbol = resolved.classSymbol
typeArguments.addAll(qualifierStack.take(qualifierParts.size).flatMap { it.typeArguments })
}.apply {
resultType = components.typeForQualifier(this)
}
}
return null
}
}
@@ -0,0 +1,182 @@
/*
* Copyright 2010-2019 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.declarations.FirClass
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
import org.jetbrains.kotlin.fir.expressions.builder.buildResolvedQualifier
import org.jetbrains.kotlin.fir.references.impl.FirSimpleNamedReference
import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeDeprecated
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType
import org.jetbrains.kotlin.fir.resolve.typeForQualifier
import org.jetbrains.kotlin.fir.scopes.getSingleClassifier
import org.jetbrains.kotlin.fir.scopes.impl.createCurrentScopeList
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.types.FirTypeProjection
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
const val ROOT_PREFIX_FOR_IDE_RESOLUTION_MODE = "_root_ide_package_"
fun BodyResolveComponents.resolveRootPartOfQualifier(
namedReference: FirSimpleNamedReference,
source: FirSourceElement?,
typeArguments: List<FirTypeProjection>,
nonFatalDiagnosticsFromExpression: List<ConeDiagnostic>?,
): FirResolvedQualifier? {
val name = namedReference.name
if (name.asString() == ROOT_PREFIX_FOR_IDE_RESOLUTION_MODE) {
return buildResolvedQualifier {
this.source = source
packageFqName = FqName.ROOT
this.nonFatalDiagnostics.addAll(nonFatalDiagnosticsFromExpression.orEmpty())
}.apply {
resultType = typeForQualifier(this)
}
}
for (scope in createCurrentScopeList()) {
scope.getSingleClassifier(name)?.let {
if (it is FirRegularClassSymbol) {
val classId = it.classId
return buildResolvedQualifier {
this.source = source
packageFqName = classId.packageFqName
relativeClassFqName = classId.relativeClassName
symbol = it
this.typeArguments.addAll(typeArguments)
this.nonFatalDiagnostics.addAll(
extractNonFatalDiagnostics(
source,
explicitReceiver = null,
it,
extraNotFatalDiagnostics = nonFatalDiagnosticsFromExpression
)
)
}.apply {
resultType = typeForQualifier(this)
}
}
}
}
return FqName.ROOT.continueQualifierInPackage(name, typeArguments, nonFatalDiagnosticsFromExpression, this, source)
}
fun FirResolvedQualifier.continueQualifier(
namedReference: FirSimpleNamedReference,
source: FirSourceElement?,
typeArguments: List<FirTypeProjection>,
nonFatalDiagnosticsFromExpression: List<ConeDiagnostic>?,
components: BodyResolveComponents,
): FirResolvedQualifier? {
val name = namedReference.name
symbol?.let { outerClassSymbol ->
val firClass = outerClassSymbol.fir
if (firClass !is FirClass) return null
return firClass.scopeProvider.getNestedClassifierScope(firClass, components.session, components.scopeSession)
?.getSingleClassifier(name)
?.takeIf { it is FirClassLikeSymbol<*> }
?.let { nestedClassSymbol ->
buildResolvedQualifier {
this.source = source
packageFqName = this@continueQualifier.packageFqName
relativeClassFqName = this@continueQualifier.relativeClassFqName?.child(name)
symbol = nestedClassSymbol as FirClassLikeSymbol<*>
val outerTypeArguments = this.typeArguments.toList()
this.typeArguments.clear()
this.typeArguments.addAll(typeArguments)
this.typeArguments.addAll(outerTypeArguments)
this.nonFatalDiagnostics.addAll(nonFatalDiagnosticsFromExpression.orEmpty())
this.nonFatalDiagnostics.addAll(
extractNonFatalDiagnostics(
source,
explicitReceiver = null,
nestedClassSymbol,
extraNotFatalDiagnostics = this@continueQualifier.nonFatalDiagnostics
)
)
}.apply {
resultType = components.typeForQualifier(this)
}
}
}
return packageFqName.continueQualifierInPackage(name, typeArguments, nonFatalDiagnosticsFromExpression, components, source)
}
private fun FqName.continueQualifierInPackage(
name: Name,
typeArguments: List<FirTypeProjection>,
nonFatalDiagnosticsFromExpression: List<ConeDiagnostic>?,
components: BodyResolveComponents,
source: FirSourceElement?
): FirResolvedQualifier? {
val childFqName = this.child(name)
if (components.symbolProvider.getPackage(childFqName) != null) {
return buildResolvedQualifier {
this.source = source
packageFqName = childFqName
this.typeArguments.addAll(typeArguments)
this.nonFatalDiagnostics.addAll(nonFatalDiagnosticsFromExpression.orEmpty())
}.apply {
resultType = components.typeForQualifier(this)
}
}
val classId = ClassId.topLevel(childFqName)
val symbol = components.symbolProvider.getClassLikeSymbolByClassId(classId) ?: return null
return buildResolvedQualifier {
this.source = source
packageFqName = this@continueQualifierInPackage
relativeClassFqName = classId.relativeClassName
this.symbol = symbol
this.typeArguments.addAll(typeArguments)
this.nonFatalDiagnostics.addAll(
extractNonFatalDiagnostics(
source,
explicitReceiver = null,
symbol,
extraNotFatalDiagnostics = nonFatalDiagnosticsFromExpression
)
)
}.apply {
resultType = components.typeForQualifier(this)
}
}
internal fun extractNonFatalDiagnostics(
source: FirSourceElement?,
explicitReceiver: FirExpression?,
symbol: FirClassLikeSymbol<*>,
extraNotFatalDiagnostics: List<ConeDiagnostic>?
): List<ConeDiagnostic> {
val prevDiagnostics = (explicitReceiver as? FirResolvedQualifier)?.nonFatalDiagnostics ?: emptyList()
var result: MutableList<ConeDiagnostic>? = null
val deprecation = symbol.fir.deprecation?.forUseSite()
if (deprecation != null) {
result = mutableListOf()
result.addAll(prevDiagnostics)
result.add(ConeDeprecated(source, symbol, deprecation))
}
if (extraNotFatalDiagnostics != null && extraNotFatalDiagnostics.isNotEmpty()) {
if (result == null) {
result = mutableListOf()
result.addAll(prevDiagnostics)
}
result.addAll(extraNotFatalDiagnostics)
}
return result?.toList() ?: prevDiagnostics
}
@@ -75,7 +75,6 @@ class CandidateFactory private constructor(
result.addDiagnostic(Unsupported("References to variables aren't supported yet", callSite.calleeReference.source))
}
} else if (objectsByName &&
context.bodyResolveContext.qualifierPartIndexFromEnd == 0 &&
symbol is FirRegularClassSymbol &&
symbol.classKind != ClassKind.OBJECT &&
symbol.companionObjectSymbol == null
@@ -16,10 +16,7 @@ import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProviderInternals
import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitor
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.*
@ThreadSafeMutableState
class FirProviderImpl(val session: FirSession, val kotlinScopeProvider: FirKotlinScopeProvider) : FirProvider() {
@@ -75,7 +72,7 @@ class FirProviderImpl(val session: FirSession, val kotlinScopeProvider: FirKotli
}
override fun getPackage(fqName: FqName): FqName? {
if (getFirFilesByPackage(fqName).isNotEmpty()) return fqName
if (fqName in state.allSubPackages) return fqName
return null
}
}
@@ -100,6 +97,7 @@ class FirProviderImpl(val session: FirSession, val kotlinScopeProvider: FirKotli
private fun recordFile(file: FirFile, state: State) {
val packageName = file.packageFqName
state.fileMap.merge(packageName, listOf(file)) { a, b -> a + b }
generateSequence(packageName) { it.parentOrNull() }.forEach(state.allSubPackages::add)
file.acceptChildren(FirRecorder, FirRecorderData(state, file, session.nameConflictsTracker))
}
@@ -179,6 +177,7 @@ class FirProviderImpl(val session: FirSession, val kotlinScopeProvider: FirKotli
private class State {
val fileMap = mutableMapOf<FqName, List<FirFile>>()
val allSubPackages = mutableSetOf<FqName>()
val classifierMap = mutableMapOf<ClassId, FirClassLikeDeclaration>()
val classifierContainerFileMap = mutableMapOf<ClassId, FirFile>()
val classesInPackage = mutableMapOf<FqName, MutableSet<Name>>()
@@ -189,6 +188,7 @@ class FirProviderImpl(val session: FirSession, val kotlinScopeProvider: FirKotli
fun setFrom(other: State) {
fileMap.clear()
allSubPackages.clear()
classifierMap.clear()
classifierContainerFileMap.clear()
functionMap.clear()
@@ -197,6 +197,7 @@ class FirProviderImpl(val session: FirSession, val kotlinScopeProvider: FirKotli
callableContainerMap.clear()
fileMap.putAll(other.fileMap)
allSubPackages.addAll(other.allSubPackages)
classifierMap.putAll(other.classifierMap)
classifierContainerFileMap.putAll(other.classifierContainerFileMap)
functionMap.putAll(other.functionMap)
@@ -6,7 +6,6 @@
package org.jetbrains.kotlin.fir.resolve.transformers.body.resolve
import org.jetbrains.kotlin.fir.FirCallResolver
import org.jetbrains.kotlin.fir.FirQualifiedNameResolver
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.PrivateForInline
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
@@ -101,10 +100,8 @@ abstract class FirAbstractBodyResolveTransformer(phase: FirResolvePhase) : FirAb
override val resolutionStageRunner: ResolutionStageRunner = ResolutionStageRunner()
private val qualifiedResolver: FirQualifiedNameResolver = FirQualifiedNameResolver(this)
override val callResolver: FirCallResolver = FirCallResolver(
this,
qualifiedResolver
)
val typeResolverTransformer = FirSpecificTypeResolverTransformer(
session
@@ -38,6 +38,7 @@ import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.visitors.FirDefaultTransformer
import org.jetbrains.kotlin.fir.visitors.FirTransformer
import org.jetbrains.kotlin.fir.visitors.TransformData
import org.jetbrains.kotlin.fir.visitors.transformSingle
import org.jetbrains.kotlin.name.Name
@@ -77,6 +78,12 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
override fun transformQualifiedAccessExpression(
qualifiedAccessExpression: FirQualifiedAccessExpression,
data: ResolutionMode,
): FirStatement = transformQualifiedAccessExpression(qualifiedAccessExpression, data, isUsedAsReceiver = false)
fun transformQualifiedAccessExpression(
qualifiedAccessExpression: FirQualifiedAccessExpression,
data: ResolutionMode,
isUsedAsReceiver: Boolean,
): FirStatement {
qualifiedAccessExpression.transformAnnotations(this, data)
qualifiedAccessExpression.transformTypeArguments(transformer, ResolutionMode.ContextIndependent)
@@ -130,7 +137,7 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
qualifiedAccessExpression
}
else -> {
val transformedCallee = resolveQualifiedAccessAndSelectCandidate(qualifiedAccessExpression)
val transformedCallee = resolveQualifiedAccessAndSelectCandidate(qualifiedAccessExpression, isUsedAsReceiver)
// NB: here we can get raw expression because of dropped qualifiers (see transform callee),
// so candidate existence must be checked before calling completion
if (transformedCallee is FirQualifiedAccessExpression && transformedCallee.candidate() != null) {
@@ -163,8 +170,11 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
return transformQualifiedAccessExpression(propertyAccessExpression, data)
}
protected open fun resolveQualifiedAccessAndSelectCandidate(qualifiedAccessExpression: FirQualifiedAccessExpression): FirStatement {
return callResolver.resolveVariableAccessAndSelectCandidate(qualifiedAccessExpression)
protected open fun resolveQualifiedAccessAndSelectCandidate(
qualifiedAccessExpression: FirQualifiedAccessExpression,
isUsedAsReceiver: Boolean,
): FirStatement {
return callResolver.resolveVariableAccessAndSelectCandidate(qualifiedAccessExpression, isUsedAsReceiver)
}
fun transformSuperReceiver(
@@ -752,7 +762,7 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
): FirStatement {
// val resolvedAssignment = transformCallee(variableAssignment)
variableAssignment.transformAnnotations(transformer, ResolutionMode.ContextIndependent)
val resolvedAssignment = callResolver.resolveVariableAccessAndSelectCandidate(variableAssignment)
val resolvedAssignment = callResolver.resolveVariableAccessAndSelectCandidate(variableAssignment, isUsedAsReceiver = false)
val result = if (resolvedAssignment is FirVariableAssignment) {
val completeAssignment = callCompleter.completeCall(resolvedAssignment, noExpectedType).result // TODO: check
completeAssignment.transformRValue(
@@ -778,7 +788,14 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
callableReferenceAccess.transformAnnotations(transformer, data)
val explicitReceiver = callableReferenceAccess.explicitReceiver
val transformedLHS = context.withIncrementedQualifierPartIndex {
explicitReceiver?.transformSingle(this, ResolutionMode.ContextIndependent)?.apply {
if (explicitReceiver is FirPropertyAccessExpression) {
transformQualifiedAccessExpression(
explicitReceiver, ResolutionMode.ContextIndependent, isUsedAsReceiver = true
) as FirExpression
} else {
explicitReceiver?.transformSingle(this, ResolutionMode.ContextIndependent)
}.apply {
if (this is FirResolvedQualifier && callableReferenceAccess.hasQuestionMarkAtLHS) {
replaceIsNullableLHSForCallableReference(true)
}
@@ -811,7 +828,19 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
}
val transformedGetClassCall = context.withIncrementedQualifierPartIndex {
transformExpression(getClassCall, dataWithExpectedType) as FirGetClassCall
val argument = getClassCall.argument
val replacedArgument: FirExpression =
if (argument is FirPropertyAccessExpression)
transformQualifiedAccessExpression(argument, dataWithExpectedType, isUsedAsReceiver = true) as FirExpression
else
argument.transform(this, dataWithExpectedType)
getClassCall.argumentList.transformArguments(object : FirTransformer<Nothing?>() {
@Suppress("UNCHECKED_CAST")
override fun <E : FirElement> transformElement(element: E, data: Nothing?): E = replacedArgument as E
}, null)
getClassCall
}
val typeOfExpression = when (val lhs = transformedGetClassCall.argument) {
@@ -156,8 +156,11 @@ private class FirExpressionsResolveTransformerForSpecificAnnotations(
return calleeReference !is FirErrorNamedReference
}
override fun resolveQualifiedAccessAndSelectCandidate(qualifiedAccessExpression: FirQualifiedAccessExpression): FirStatement {
return callResolver.resolveOnlyEnumOrQualifierAccessAndSelectCandidate(qualifiedAccessExpression)
override fun resolveQualifiedAccessAndSelectCandidate(
qualifiedAccessExpression: FirQualifiedAccessExpression,
isUsedAsReceiver: Boolean,
): FirStatement {
return callResolver.resolveOnlyEnumOrQualifierAccessAndSelectCandidate(qualifiedAccessExpression, isUsedAsReceiver)
}
override fun transformFunctionCall(functionCall: FirFunctionCall, data: ResolutionMode): FirStatement {
@@ -6,7 +6,7 @@ val s = <!EXPRESSION_EXPECTED_PACKAGE_FOUND!>java<!>
val ss = <!NO_COMPANION_OBJECT!>System<!>
val sss = <!NO_COMPANION_OBJECT!>X<!>
val x = "${<!NO_COMPANION_OBJECT!>System<!>}"
val xs = java.<!EXPRESSION_EXPECTED_PACKAGE_FOUND!>lang<!>
val xs = java.<!UNRESOLVED_REFERENCE!>lang<!>
val xss = java.lang.<!NO_COMPANION_OBJECT!>System<!>
val xsss = foo.<!NO_COMPANION_OBJECT!>X<!>
val xssss = <!EXPRESSION_EXPECTED_PACKAGE_FOUND!>foo<!>
@@ -0,0 +1,31 @@
// FIR_IDENTICAL
// SKIP_TXT
// FILE: test/W.java
package test;
public class W {
public void foo() {}
public static void bar() {}
}
// FILE: main.kt
package main1
import test.W
val W: W = W()
fun main() {
W.foo()
W.<!UNRESOLVED_REFERENCE!>bar<!>()
W().foo()
}
// FILE: main2.kt
package main2
import test.W
fun main() {
W.<!UNRESOLVED_REFERENCE!>foo<!>()
W.bar()
W().foo()
}
@@ -22647,6 +22647,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
runTest("compiler/testData/diagnostics/tests/qualifiedExpression/TypeWithError.kt");
}
@Test
@TestMetadata("variableVsImportedClassifier.kt")
public void testVariableVsImportedClassifier() throws Exception {
runTest("compiler/testData/diagnostics/tests/qualifiedExpression/variableVsImportedClassifier.kt");
}
@Test
@TestMetadata("visibleClassVsQualifiedClass.kt")
public void testVisibleClassVsQualifiedClass() throws Exception {
@@ -1,5 +1,5 @@
KT element: KtNameReferenceExpression
FIR element: FirFunctionCallImpl
FIR element: FirResolvedQualifierImpl
FIR element rendered:
Q|A.B|.R|/A.B.C.C|()
Q|A.B|
@@ -1,5 +1,5 @@
KT element: KtDotQualifiedExpression
FIR element: FirFunctionCallImpl
FIR element: FirResolvedQualifierImpl
FIR element rendered:
Q|A.B|.R|/A.B.C.C|()
Q|A.B|
@@ -22641,6 +22641,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
runTest("compiler/testData/diagnostics/tests/qualifiedExpression/TypeWithError.kt");
}
@Test
@TestMetadata("variableVsImportedClassifier.kt")
public void testVariableVsImportedClassifier() throws Exception {
runTest("compiler/testData/diagnostics/tests/qualifiedExpression/variableVsImportedClassifier.kt");
}
@Test
@TestMetadata("visibleClassVsQualifiedClass.kt")
public void testVisibleClassVsQualifiedClass() throws Exception {
@@ -1,2 +1,2 @@
Resolved to:
0: (in t.A.Named) val i: kotlin.Int
0: (in t) class A