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:
committed by
teamcityserver
parent
69f7c2278d
commit
9575d4d242
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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
|
||||
|
||||
+6
-5
@@ -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)
|
||||
|
||||
-3
@@ -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
|
||||
|
||||
+35
-6
@@ -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) {
|
||||
|
||||
+5
-2
@@ -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<!>
|
||||
|
||||
+31
@@ -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()
|
||||
}
|
||||
Generated
+6
@@ -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 {
|
||||
|
||||
+2
-2
@@ -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|
|
||||
|
||||
+2
-2
@@ -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|
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
Vendored
+1
-1
@@ -1,2 +1,2 @@
|
||||
Resolved to:
|
||||
0: (in t.A.Named) val i: kotlin.Int
|
||||
0: (in t) class A
|
||||
|
||||
Reference in New Issue
Block a user