[FIR] KT-45972, KT-49072: Add visibility checks to the type resolution

This commit is contained in:
Nikolay Lunyak
2021-10-15 16:42:41 +03:00
committed by TeamCityServer
parent 2dc2a90755
commit 0eb2d117ef
17 changed files with 223 additions and 42 deletions
@@ -12477,6 +12477,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
runTest("compiler/testData/diagnostics/tests/imports/InaccessiblePrivateClass.kt"); runTest("compiler/testData/diagnostics/tests/imports/InaccessiblePrivateClass.kt");
} }
@Test
@TestMetadata("InaccessiblePrivateInFile.kt")
public void testInaccessiblePrivateInFile() throws Exception {
runTest("compiler/testData/diagnostics/tests/imports/InaccessiblePrivateInFile.kt");
}
@Test @Test
@TestMetadata("invisibleFakeReferenceInImport.kt") @TestMetadata("invisibleFakeReferenceInImport.kt")
public void testInvisibleFakeReferenceInImport() throws Exception { public void testInvisibleFakeReferenceInImport() throws Exception {
@@ -12477,6 +12477,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
runTest("compiler/testData/diagnostics/tests/imports/InaccessiblePrivateClass.kt"); runTest("compiler/testData/diagnostics/tests/imports/InaccessiblePrivateClass.kt");
} }
@Test
@TestMetadata("InaccessiblePrivateInFile.kt")
public void testInaccessiblePrivateInFile() throws Exception {
runTest("compiler/testData/diagnostics/tests/imports/InaccessiblePrivateInFile.kt");
}
@Test @Test
@TestMetadata("invisibleFakeReferenceInImport.kt") @TestMetadata("invisibleFakeReferenceInImport.kt")
public void testInvisibleFakeReferenceInImport() throws Exception { public void testInvisibleFakeReferenceInImport() throws Exception {
@@ -12477,6 +12477,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
runTest("compiler/testData/diagnostics/tests/imports/InaccessiblePrivateClass.kt"); runTest("compiler/testData/diagnostics/tests/imports/InaccessiblePrivateClass.kt");
} }
@Test
@TestMetadata("InaccessiblePrivateInFile.kt")
public void testInaccessiblePrivateInFile() throws Exception {
runTest("compiler/testData/diagnostics/tests/imports/InaccessiblePrivateInFile.kt");
}
@Test @Test
@TestMetadata("invisibleFakeReferenceInImport.kt") @TestMetadata("invisibleFakeReferenceInImport.kt")
public void testInvisibleFakeReferenceInImport() throws Exception { public void testInvisibleFakeReferenceInImport() throws Exception {
@@ -7,12 +7,19 @@ package org.jetbrains.kotlin.fir.resolve
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSessionComponent import org.jetbrains.kotlin.fir.FirSessionComponent
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.resolve.transformers.ScopeClassDeclaration import org.jetbrains.kotlin.fir.resolve.transformers.ScopeClassDeclaration
import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.FirTypeRef import org.jetbrains.kotlin.fir.types.FirTypeRef
abstract class FirTypeResolver : FirSessionComponent { abstract class FirTypeResolver : FirSessionComponent {
abstract fun resolveType(typeRef: FirTypeRef, scopeClassDeclaration: ScopeClassDeclaration, areBareTypesAllowed: Boolean, isOperandOfIsOperator: Boolean): ConeKotlinType abstract fun resolveType(
typeRef: FirTypeRef,
scopeClassDeclaration: ScopeClassDeclaration,
areBareTypesAllowed: Boolean,
isOperandOfIsOperator: Boolean,
useSiteFile: FirFile?
): ConeKotlinType
} }
val FirSession.typeResolver: FirTypeResolver by FirSession.sessionComponentAccessor() val FirSession.typeResolver: FirTypeResolver by FirSession.sessionComponentAccessor()
@@ -7,8 +7,7 @@ package org.jetbrains.kotlin.fir.resolve.providers.impl
import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.FirEnumEntry import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.impl.FirOuterClassTypeParameterRef import org.jetbrains.kotlin.fir.declarations.impl.FirOuterClassTypeParameterRef
import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass
import org.jetbrains.kotlin.fir.declarations.utils.isLocal import org.jetbrains.kotlin.fir.declarations.utils.isLocal
@@ -23,7 +22,6 @@ import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeWrongNumberOfTypeArgumen
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.transformers.ScopeClassDeclaration import org.jetbrains.kotlin.fir.resolve.transformers.ScopeClassDeclaration
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.SymbolInternals import org.jetbrains.kotlin.fir.symbols.SymbolInternals
@@ -54,9 +52,52 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
} }
} }
private fun resolveSymbol(
symbol: FirBasedSymbol<*>,
qualifier: List<FirQualifierPart>,
qualifierResolver: FirQualifierResolver,
): FirBasedSymbol<*>? {
return when (symbol) {
is FirClassLikeSymbol<*> -> {
if (qualifier.size == 1) {
symbol
} else {
resolveLocalClassChain(symbol, qualifier)
?: qualifierResolver.resolveSymbolWithPrefix(qualifier, symbol.classId)
?: qualifierResolver.resolveEnumEntrySymbol(qualifier, symbol.classId)
}
}
is FirTypeParameterSymbol -> {
assert(qualifier.size == 1)
symbol
}
else -> error("!")
}
}
private fun FirBasedSymbol<*>?.isVisible(
useSiteFile: FirFile?,
containingDeclarations: List<FirDeclaration>,
): Boolean {
val declaration = this?.fir
return if (useSiteFile != null && declaration is FirMemberDeclaration) {
session.visibilityChecker.isVisible(
declaration,
session,
useSiteFile,
containingDeclarations,
null,
false,
)
} else {
true
}
}
private fun resolveToSymbol( private fun resolveToSymbol(
typeRef: FirTypeRef, typeRef: FirTypeRef,
scopes: List<FirScope>, scopeClassDeclaration: ScopeClassDeclaration,
useSiteFile: FirFile?,
): Pair<FirBasedSymbol<*>?, ConeSubstitutor?> { ): Pair<FirBasedSymbol<*>?, ConeSubstitutor?> {
return when (typeRef) { return when (typeRef) {
is FirResolvedTypeRef -> { is FirResolvedTypeRef -> {
@@ -66,37 +107,39 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
is FirUserTypeRef -> { is FirUserTypeRef -> {
val qualifierResolver = session.qualifierResolver val qualifierResolver = session.qualifierResolver
var resolvedSymbol: FirBasedSymbol<*>? = null var acceptedSymbol: FirBasedSymbol<*>? = null
var firstNonApplicable: FirBasedSymbol<*>? = null
var substitutor: ConeSubstitutor? = null var substitutor: ConeSubstitutor? = null
val qualifier = typeRef.qualifier val qualifier = typeRef.qualifier
val scopes = scopeClassDeclaration.scopes
val containingDeclarations = scopeClassDeclaration.containingDeclarations
for (scope in scopes) { for (scope in scopes) {
if (resolvedSymbol != null) { if (acceptedSymbol != null) {
break break
} }
scope.processClassifiersByNameWithSubstitution(qualifier.first().name) { symbol, substitutorFromScope -> scope.processClassifiersByNameWithSubstitution(qualifier.first().name) { symbol, substitutorFromScope ->
if (resolvedSymbol != null) return@processClassifiersByNameWithSubstitution if (acceptedSymbol != null) {
resolvedSymbol = when (symbol) { return@processClassifiersByNameWithSubstitution
is FirClassLikeSymbol<*> -> { }
if (qualifier.size == 1) {
symbol val resolvedSymbol = resolveSymbol(symbol, qualifier, qualifierResolver)
} else {
resolveLocalClassChain(symbol, qualifier) if (resolvedSymbol.isVisible(useSiteFile, containingDeclarations)) {
?: qualifierResolver.resolveSymbolWithPrefix(qualifier, symbol.classId) acceptedSymbol = resolvedSymbol
?: qualifierResolver.resolveEnumEntrySymbol(qualifier, symbol.classId) substitutor = substitutorFromScope
} } else {
} firstNonApplicable = resolvedSymbol
is FirTypeParameterSymbol -> {
assert(qualifier.size == 1)
symbol
}
else -> error("!")
} }
substitutor = substitutorFromScope
} }
} }
if (acceptedSymbol == null) {
acceptedSymbol = firstNonApplicable
}
// TODO: Imports // TODO: Imports
val resultSymbol: FirBasedSymbol<*>? = resolvedSymbol ?: qualifierResolver.resolveSymbol(qualifier) val resultSymbol: FirBasedSymbol<*>? = acceptedSymbol ?: qualifierResolver.resolveSymbol(qualifier)
resultSymbol to substitutor resultSymbol to substitutor
} }
@@ -377,18 +420,19 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
typeRef: FirTypeRef, typeRef: FirTypeRef,
scopeClassDeclaration: ScopeClassDeclaration, scopeClassDeclaration: ScopeClassDeclaration,
areBareTypesAllowed: Boolean, areBareTypesAllowed: Boolean,
isOperandOfIsOperator: Boolean isOperandOfIsOperator: Boolean,
useSiteFile: FirFile?,
): ConeKotlinType { ): ConeKotlinType {
return when (typeRef) { return when (typeRef) {
is FirResolvedTypeRef -> typeRef.type is FirResolvedTypeRef -> typeRef.type
is FirUserTypeRef -> { is FirUserTypeRef -> {
val (symbol, substitutor) = resolveToSymbol(typeRef, scopeClassDeclaration.scopes) val (symbol, substitutor) = resolveToSymbol(typeRef, scopeClassDeclaration, useSiteFile)
resolveUserType( resolveUserType(
typeRef, typeRef,
symbol, symbol,
substitutor, substitutor,
areBareTypesAllowed, areBareTypesAllowed,
scopeClassDeclaration.topDeclaration, scopeClassDeclaration.containingDeclarations.lastOrNull(),
isOperandOfIsOperator isOperandOfIsOperator
) )
} }
@@ -73,7 +73,16 @@ class FirSpecificTypeResolverTransformer(
val scopeOwnerLookupNames = data.scopes.flatMap { it.scopeOwnerLookupNames } val scopeOwnerLookupNames = data.scopes.flatMap { it.scopeOwnerLookupNames }
session.lookupTracker?.recordTypeLookup(typeRef, scopeOwnerLookupNames, currentFile?.source) session.lookupTracker?.recordTypeLookup(typeRef, scopeOwnerLookupNames, currentFile?.source)
typeRef.transformChildren(this, data) typeRef.transformChildren(this, data)
return transformType(typeRef, typeResolver.resolveType(typeRef, data, areBareTypesAllowed, isOperandOfIsOperator)) return transformType(
typeRef,
typeResolver.resolveType(
typeRef,
data,
areBareTypesAllowed,
isOperandOfIsOperator,
currentFile,
)
)
} }
@OptIn(PrivateForInline::class) @OptIn(PrivateForInline::class)
@@ -84,7 +93,13 @@ class FirSpecificTypeResolverTransformer(
functionTypeRef.transformChildren(this, data) functionTypeRef.transformChildren(this, data)
val scopeOwnerLookupNames = data.scopes.flatMap { it.scopeOwnerLookupNames } val scopeOwnerLookupNames = data.scopes.flatMap { it.scopeOwnerLookupNames }
session.lookupTracker?.recordTypeLookup(functionTypeRef, scopeOwnerLookupNames, currentFile?.source) session.lookupTracker?.recordTypeLookup(functionTypeRef, scopeOwnerLookupNames, currentFile?.source)
val resolvedType = typeResolver.resolveType(functionTypeRef, data, areBareTypesAllowed, isOperandOfIsOperator).takeIfAcceptable() val resolvedType = typeResolver.resolveType(
functionTypeRef,
data,
areBareTypesAllowed,
isOperandOfIsOperator,
currentFile,
).takeIfAcceptable()
return if (resolvedType != null && resolvedType !is ConeClassErrorType) { return if (resolvedType != null && resolvedType !is ConeClassErrorType) {
buildResolvedTypeRef { buildResolvedTypeRef {
source = functionTypeRef.source source = functionTypeRef.source
@@ -84,13 +84,17 @@ fun <F : FirClassLikeDeclaration> F.runSupertypeResolvePhaseForLocalClass(
currentScopeList: List<FirScope>, currentScopeList: List<FirScope>,
localClassesNavigationInfo: LocalClassesNavigationInfo, localClassesNavigationInfo: LocalClassesNavigationInfo,
firProviderInterceptor: FirProviderInterceptor?, firProviderInterceptor: FirProviderInterceptor?,
useSiteFile: FirFile,
containingDeclarations: List<FirDeclaration>,
): F { ): F {
val supertypeComputationSession = SupertypeComputationSession() val supertypeComputationSession = SupertypeComputationSession()
val supertypeResolverVisitor = FirSupertypeResolverVisitor( val supertypeResolverVisitor = FirSupertypeResolverVisitor(
session, supertypeComputationSession, scopeSession, session, supertypeComputationSession, scopeSession,
currentScopeList.toPersistentList(), currentScopeList.toPersistentList(),
localClassesNavigationInfo, localClassesNavigationInfo,
firProviderInterceptor firProviderInterceptor,
useSiteFile,
containingDeclarations,
) )
this.accept(supertypeResolverVisitor, null) this.accept(supertypeResolverVisitor, null)
@@ -204,10 +208,20 @@ open class FirSupertypeResolverVisitor(
private val scopeForLocalClass: PersistentList<FirScope>? = null, private val scopeForLocalClass: PersistentList<FirScope>? = null,
private val localClassesNavigationInfo: LocalClassesNavigationInfo? = null, private val localClassesNavigationInfo: LocalClassesNavigationInfo? = null,
private val firProviderInterceptor: FirProviderInterceptor? = null, private val firProviderInterceptor: FirProviderInterceptor? = null,
private val useSiteFile: FirFile? = null,
containingDeclarations: List<FirDeclaration> = emptyList(),
) : FirDefaultVisitor<Unit, Any?>() { ) : FirDefaultVisitor<Unit, Any?>() {
private val supertypeGenerationExtensions = session.extensionService.supertypeGenerators private val supertypeGenerationExtensions = session.extensionService.supertypeGenerators
private val classDeclarationsStack = ArrayDeque<FirRegularClass>() private val classDeclarationsStack = ArrayDeque<FirRegularClass>()
init {
containingDeclarations.forEach {
if (it is FirRegularClass) {
classDeclarationsStack.add(it)
}
}
}
private fun getFirClassifierContainerFileIfAny(symbol: FirClassLikeSymbol<*>): FirFile? = private fun getFirClassifierContainerFileIfAny(symbol: FirClassLikeSymbol<*>): FirFile? =
if (firProviderInterceptor != null) firProviderInterceptor.getFirClassifierContainerFileIfAny(symbol) if (firProviderInterceptor != null) firProviderInterceptor.getFirClassifierContainerFileIfAny(symbol)
else session.firProvider.getFirClassifierContainerFileIfAny(symbol.classId) else session.firProvider.getFirClassifierContainerFileIfAny(symbol.classId)
@@ -301,10 +315,12 @@ open class FirSupertypeResolverVisitor(
val scopes = prepareScopes(classLikeDeclaration) val scopes = prepareScopes(classLikeDeclaration)
val transformer = FirSpecificTypeResolverTransformer(session) val transformer = FirSpecificTypeResolverTransformer(session)
val resolvedTypesRefs = resolveSuperTypeRefs( val resolvedTypesRefs = transformer.withFile(useSiteFile) {
transformer, resolveSuperTypeRefs(
ScopeClassDeclaration(scopes, classDeclarationsStack.lastOrNull()) transformer,
) ScopeClassDeclaration(scopes, classDeclarationsStack)
)
}
supertypeComputationSession.storeSupertypes(classLikeDeclaration, resolvedTypesRefs) supertypeComputationSession.storeSupertypes(classLikeDeclaration, resolvedTypesRefs)
return resolvedTypesRefs return resolvedTypesRefs
@@ -178,7 +178,7 @@ open class FirTypeResolveTransformer(
return typeResolverTransformer.withFile(currentFile) { return typeResolverTransformer.withFile(currentFile) {
typeRef.transform( typeRef.transform(
typeResolverTransformer, typeResolverTransformer,
ScopeClassDeclaration(towerScope, classDeclarationsStack.lastOrNull()) ScopeClassDeclaration(towerScope, classDeclarationsStack)
) )
} }
} }
@@ -10,5 +10,5 @@ import org.jetbrains.kotlin.fir.scopes.FirScope
data class ScopeClassDeclaration( data class ScopeClassDeclaration(
val scopes: List<FirScope>, val scopes: List<FirScope>,
val topDeclaration: FirRegularClass? val containingDeclarations: List<FirRegularClass>
) )
@@ -70,7 +70,7 @@ open class FirBodyResolveTransformer(
typeRef, typeRef,
ScopeClassDeclaration( ScopeClassDeclaration(
components.createCurrentScopeList(), components.createCurrentScopeList(),
context.topClassDeclaration context.containingClassDeclarations
) )
) )
} }
@@ -37,7 +37,9 @@ fun <F : FirClassLikeDeclaration> F.runAllPhasesForLocalClass(
components.scopeSession, components.scopeSession,
components.createCurrentScopeList(), components.createCurrentScopeList(),
localClassesNavigationInfo, localClassesNavigationInfo,
firProviderInterceptor firProviderInterceptor,
components.file,
components.containingDeclarations,
) )
runTypeResolvePhaseForLocalClass( runTypeResolvePhaseForLocalClass(
components.session, components.session,
@@ -115,7 +115,7 @@ private class FirAnnotationResolveTransformer(
): FirStatement { ): FirStatement {
return annotation.transformAnnotationTypeRef( return annotation.transformAnnotationTypeRef(
typeResolverTransformer, typeResolverTransformer,
ScopeClassDeclaration(scopes, classDeclarationsStack.lastOrNull()) ScopeClassDeclaration(scopes, classDeclarationsStack)
) )
} }
@@ -14,5 +14,5 @@ package p1
import p2.* import p2.*
val <!EXPOSED_PROPERTY_TYPE!>x<!>: X = <!INITIALIZER_TYPE_MISMATCH, TYPE_MISMATCH!>X()<!> val x: X = X()
val <!EXPOSED_PROPERTY_TYPE!>y<!>: Y = <!INVISIBLE_REFERENCE!>Y<!>() val <!EXPOSED_PROPERTY_TYPE!>y<!>: Y = <!INVISIBLE_REFERENCE!>Y<!>()
@@ -0,0 +1,30 @@
// FIR_IDENTICAL
// MODULE: m1
// FILE: f11.kt
package api
interface ApplicabilityResult
// FILE: f12.kt
package api
interface ArgumentMapping {
fun highlightingApplicabilities(): ApplicabilityResult
}
// MODULE: m2(m1)
// FILE: f21.kt
package impl
private data class ApplicabilityResult(val applicable: Boolean)
// FILE: f22.kt
package impl
import api.*
class NullArgumentMapping : ArgumentMapping {
// This is api.ApplicabilityResult
override fun highlightingApplicabilities(): ApplicabilityResult = object : ApplicabilityResult {
}
}
@@ -0,0 +1,43 @@
// -- Module: <m1> --
package
package api {
public interface ApplicabilityResult {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface ArgumentMapping {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public abstract fun highlightingApplicabilities(): api.ApplicabilityResult
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
// -- Module: <m2> --
package
package impl {
private final data class ApplicabilityResult {
public constructor ApplicabilityResult(/*0*/ applicable: kotlin.Boolean)
public final val applicable: kotlin.Boolean
public final operator /*synthesized*/ fun component1(): kotlin.Boolean
public final /*synthesized*/ fun copy(/*0*/ applicable: kotlin.Boolean = ...): impl.ApplicabilityResult
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
public final class NullArgumentMapping : api.ArgumentMapping {
public constructor NullArgumentMapping()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ fun highlightingApplicabilities(): api.ApplicabilityResult
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
@@ -10,6 +10,6 @@ package kotlin.sequences
import p.* import p.*
interface I { interface I {
val v1: <!WRONG_NUMBER_OF_TYPE_ARGUMENTS!>FilteringSequence<!> val v1: FilteringSequence
val <!EXPOSED_PROPERTY_TYPE!>v2<!>: IndexingSequence<String> val <!EXPOSED_PROPERTY_TYPE!>v2<!>: IndexingSequence<String>
} }
@@ -12483,6 +12483,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
runTest("compiler/testData/diagnostics/tests/imports/InaccessiblePrivateClass.kt"); runTest("compiler/testData/diagnostics/tests/imports/InaccessiblePrivateClass.kt");
} }
@Test
@TestMetadata("InaccessiblePrivateInFile.kt")
public void testInaccessiblePrivateInFile() throws Exception {
runTest("compiler/testData/diagnostics/tests/imports/InaccessiblePrivateInFile.kt");
}
@Test @Test
@TestMetadata("invisibleFakeReferenceInImport.kt") @TestMetadata("invisibleFakeReferenceInImport.kt")
public void testInvisibleFakeReferenceInImport() throws Exception { public void testInvisibleFakeReferenceInImport() throws Exception {