FIR checker: check super reference

This change touches the following diagnostics to make them behave closer
to FE1.0

* SUPER_NOT_AVAILABLE
* SUPER_IS_NOT_AN_EXPRESSION
* INSTANCE_ACCESS_BEFORE_SUPER_CALL
* NOT_A_SUPERTYPE

Other than tweaking the diagnostics, this change also alters resolution
by consider marking `super` with mismatched type parameter as
errorenous. As a result, the following code no longer resolves.

```
class A: B() {
  fun test() {
    super<String>.length
    //            ^^^^^^ FIR currently resolves this to `String.length`.
    //                   With this change, `length` becomes unresolved
    //                   instead
  }
}
```

Also, now we report `UNRESOLVED_LABEL` on unresolved label on `super`
reference, though FE1.0 reports `UNRESOLVED_REFERENCE`.

All the errors above are reported as ConeDiagnostics and hence some
checkers are deleted.

In addition, it also suppresses more downstream (mostly unresolved)
errors if the receiver has errors. FE1.0 doesn't do it for all the cases
we have here. But it seems nicer to reduce these "redundant" unresolved
errors.
This commit is contained in:
Tianyu Geng
2021-06-23 16:15:02 -07:00
committed by Ivan Kochurkin
parent bcf6202863
commit 280c445783
45 changed files with 257 additions and 189 deletions
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.resolve.transformers.body.resolve
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
import org.jetbrains.kotlin.fir.diagnostics.ConeStubDiagnostic
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
@@ -49,6 +50,7 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
private inline val builtinTypes: BuiltinTypes get() = session.builtinTypes
private val arrayOfCallTransformer = FirArrayOfCallTransformer()
var enableArrayOfCallTransformation = false
var containingSafeCallExpression: FirSafeCallExpression? = null
init {
@Suppress("LeakingThis")
@@ -106,7 +108,11 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
qualifiedAccessExpression
}
is FirSuperReference -> {
transformSuperReceiver(callee, qualifiedAccessExpression, null)
transformSuperReceiver(
callee,
qualifiedAccessExpression,
containingSafeCallExpression?.takeIf { qualifiedAccessExpression == it.receiver }?.regularQualifiedAccess
)
}
is FirDelegateFieldReference -> {
val delegateFieldSymbol = callee.resolvedSymbol
@@ -157,17 +163,45 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
containingCall: FirQualifiedAccess?
): FirQualifiedAccessExpression {
val labelName = superReference.labelName
val lastDispatchReceiver = implicitReceiverStack.lastDispatchReceiver()
val implicitReceiver =
if (labelName != null) implicitReceiverStack[labelName] as? ImplicitDispatchReceiverValue
else implicitReceiverStack.lastDispatchReceiver()
// Only report label issues if the label is set and the receiver stack is not empty
if (labelName != null && lastDispatchReceiver != null) {
val labeledReceiver = implicitReceiverStack[labelName] as? ImplicitDispatchReceiverValue
if (labeledReceiver == null) {
return markSuperReferenceError(
ConeSimpleDiagnostic("Unresolved label", DiagnosticKind.UnresolvedLabel),
superReferenceContainer,
superReference
)
}
labeledReceiver
} else {
lastDispatchReceiver
}
implicitReceiver?.receiverExpression?.let {
superReferenceContainer.transformDispatchReceiver(StoreReceiver, it)
}
when (val superTypeRef = superReference.superTypeRef) {
is FirResolvedTypeRef -> {
superReferenceContainer.resultType = superTypeRef
val superTypeRefs = implicitReceiver?.boundSymbol?.fir?.superTypeRefs
val superTypeRef = superReference.superTypeRef
when {
containingCall == null -> {
val superNotAllowedDiagnostic = ConeSimpleDiagnostic("Super not allowed", DiagnosticKind.SuperNotAllowed)
return markSuperReferenceError(superNotAllowedDiagnostic, superReferenceContainer, superReference)
}
!is FirImplicitTypeRef -> {
implicitReceiver == null || superTypeRefs == null || superTypeRefs.isEmpty() -> {
val diagnostic =
if (implicitReceiverStack.lastOrNull() is InaccessibleImplicitReceiverValue) {
ConeInstanceAccessBeforeSuperCall("<super>")
} else {
ConeSimpleDiagnostic("Super not available", DiagnosticKind.SuperNotAvailable)
}
return markSuperReferenceError(diagnostic, superReferenceContainer, superReference)
}
superTypeRef is FirResolvedTypeRef -> {
superReferenceContainer.resultType = superTypeRef.copyWithNewSourceKind(FirFakeSourceElementKind.SuperCallExplicitType)
}
superTypeRef !is FirImplicitTypeRef -> {
components.typeResolverTransformer.withAllowedBareTypes {
superReference.transformChildren(transformer, ResolutionMode.ContextIndependent)
}
@@ -175,15 +209,16 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
val actualSuperType = (superReference.superTypeRef.coneType as? ConeClassLikeType)
?.fullyExpandedType(session)?.let { superType ->
val classId = superType.lookupTag.classId
val superTypeRefs = implicitReceiver?.boundSymbol?.fir?.superTypeRefs
val correspondingDeclaredSuperType = superTypeRefs?.firstOrNull {
val correspondingDeclaredSuperType = superTypeRefs.firstOrNull {
it.coneType.fullyExpandedType(session).classId == classId
}?.coneTypeSafe<ConeClassLikeType>()?.fullyExpandedType(session) ?: return@let superType
}?.coneTypeSafe<ConeClassLikeType>()?.fullyExpandedType(session) ?: return@let null
if (superType.typeArguments.isEmpty() && correspondingDeclaredSuperType.typeArguments.isNotEmpty()) {
superType.withArguments(correspondingDeclaredSuperType.typeArguments)
if (superType.typeArguments.isEmpty() && correspondingDeclaredSuperType.typeArguments.isNotEmpty() ||
superType == correspondingDeclaredSuperType
) {
correspondingDeclaredSuperType
} else {
superType
null
}
}
/*
@@ -191,48 +226,54 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
* DiagnosticsTestGenerated$Tests$ThisAndSuper.testGenericQualifiedSuperOverridden
* DiagnosticsTestGenerated$Tests$ThisAndSuper.testQualifiedSuperOverridden
*/
val actualSuperTypeRef = actualSuperType?.let {
it.toFirResolvedTypeRef(superTypeRef.source)
} ?: buildErrorTypeRef {
val actualSuperTypeRef = actualSuperType?.toFirResolvedTypeRef(superTypeRef.source) ?: buildErrorTypeRef {
source = superTypeRef.source
diagnostic = ConeSimpleDiagnostic("Not a super type", DiagnosticKind.Other)
diagnostic = ConeSimpleDiagnostic("Not a super type", DiagnosticKind.NotASupertype)
}
superReferenceContainer.resultType =
actualSuperTypeRef.copyWithNewSourceKind(FirFakeSourceElementKind.SuperCallExplicitType)
superReference.replaceSuperTypeRef(actualSuperTypeRef)
superReferenceContainer.resultType = actualSuperTypeRef
}
else -> {
val superTypeRefs = implicitReceiver?.boundSymbol?.fir?.superTypeRefs
val resultType = when {
superTypeRefs?.isNotEmpty() != true || containingCall == null -> {
buildErrorTypeRef {
source = superReferenceContainer.source
// NB: NOT_A_SUPERTYPE is reported by a separate checker
diagnostic = ConeStubDiagnostic(ConeSimpleDiagnostic("No super type", DiagnosticKind.Other))
}
val types = components.findTypesForSuperCandidates(superTypeRefs, containingCall)
val resultType = if (types.size == 1) {
// NB: NOT_A_SUPERTYPE is reported by a separate checker
buildResolvedTypeRef {
source = superReferenceContainer.source?.fakeElement(FirFakeSourceElementKind.SuperCallImplicitType)
type = types.single()
}
else -> {
val types = components.findTypesForSuperCandidates(superTypeRefs, containingCall)
if (types.size == 1)
buildResolvedTypeRef {
source = superReferenceContainer.source?.fakeElement(FirFakeSourceElementKind.SuperCallImplicitType)
type = types.single()
}
else
buildErrorTypeRef {
source = superReferenceContainer.source
// NB: NOT_A_SUPERTYPE is reported by a separate checker
diagnostic = ConeStubDiagnostic(ConeSimpleDiagnostic("Ambiguous supertype", DiagnosticKind.Other))
}
} else {
buildErrorTypeRef {
source = superReferenceContainer.source
// NB: NOT_A_SUPERTYPE is reported by a separate checker
diagnostic = ConeStubDiagnostic(ConeSimpleDiagnostic("Ambiguous supertype", DiagnosticKind.Other))
}
}
superReferenceContainer.resultType = resultType
superReferenceContainer.resultType =
resultType.copyWithNewSourceKind(FirFakeSourceElementKind.SuperCallExplicitType)
superReference.replaceSuperTypeRef(resultType)
}
}
return superReferenceContainer
}
private fun markSuperReferenceError(
superNotAvailableDiagnostic: ConeDiagnostic,
superReferenceContainer: FirQualifiedAccessExpression,
superReference: FirSuperReference
): FirQualifiedAccessExpression {
val resultType = buildErrorTypeRef {
diagnostic = superNotAvailableDiagnostic
}
superReferenceContainer.resultType = resultType
superReference.replaceSuperTypeRef(resultType)
superReferenceContainer.replaceCalleeReference(buildErrorNamedReference {
source = superReferenceContainer.source?.fakeElement(FirFakeSourceElementKind.ReferenceInAtomicQualifiedAccess)
diagnostic = superNotAvailableDiagnostic
})
return superReferenceContainer
}
protected open fun FirQualifiedAccessExpression.isAcceptableResolvedQualifiedAccess(): Boolean {
return true
}
@@ -241,22 +282,34 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
safeCallExpression: FirSafeCallExpression,
data: ResolutionMode
): FirStatement {
safeCallExpression.transformAnnotations(this, ResolutionMode.ContextIndependent)
safeCallExpression.transformReceiver(this, ResolutionMode.ContextIndependent)
withContainingSafeCallExpression(safeCallExpression) {
safeCallExpression.transformAnnotations(this, ResolutionMode.ContextIndependent)
safeCallExpression.transformReceiver(this, ResolutionMode.ContextIndependent)
val receiver = safeCallExpression.receiver
val receiver = safeCallExpression.receiver
dataFlowAnalyzer.enterSafeCallAfterNullCheck(safeCallExpression)
dataFlowAnalyzer.enterSafeCallAfterNullCheck(safeCallExpression)
safeCallExpression.apply {
checkedSubjectRef.value.propagateTypeFromOriginalReceiver(receiver, components.session)
transformRegularQualifiedAccess(this@FirExpressionsResolveTransformer, data)
propagateTypeFromQualifiedAccessAfterNullCheck(receiver, session)
safeCallExpression.apply {
checkedSubjectRef.value.propagateTypeFromOriginalReceiver(receiver, components.session)
transformRegularQualifiedAccess(this@FirExpressionsResolveTransformer, data)
propagateTypeFromQualifiedAccessAfterNullCheck(receiver, session)
}
dataFlowAnalyzer.exitSafeCall(safeCallExpression)
return safeCallExpression
}
}
dataFlowAnalyzer.exitSafeCall(safeCallExpression)
return safeCallExpression
private inline fun <T> withContainingSafeCallExpression(safeCallExpression: FirSafeCallExpression, block: () -> T): T {
val old = containingSafeCallExpression
try {
containingSafeCallExpression = safeCallExpression
return block()
} finally {
containingSafeCallExpression = old
}
}
override fun transformCheckedSafeCallSubject(