[FIR] Check for setter visibility in resolution stage

This is required for the following commit where candidates from the
original scope in presence of smart cast will not be ignored if they're
the same symbol.
This commit is contained in:
Kirill Rakhman
2023-12-18 11:42:29 +01:00
committed by Space Team
parent 737b286d5c
commit 5186ba80e2
15 changed files with 44 additions and 76 deletions
@@ -149,5 +149,5 @@ FILE: RedundantVisibilityModifierCheckerForInnerComponents.kt
}
public final fun test5(): R|kotlin/Unit| {
R|/B5.B5|().R|/B5.attribute| = String(c)
R|/B5.B5|().R|/B5.attribute<HIDDEN SETTER: /B5.attribute is invisible>#| = String(c)
}
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.fir.analysis.checkers.expression
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.cfa.evaluatedInPlace
@@ -15,78 +14,23 @@ import org.jetbrains.kotlin.fir.analysis.checkers.context.findClosest
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingSymbol
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.visibility
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.originalForSubstitutionOverride
import org.jetbrains.kotlin.fir.references.*
import org.jetbrains.kotlin.fir.resolve.dfa.controlFlowGraph
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeDiagnosticWithCandidates
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedNameError
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.types.resolvedType
import org.jetbrains.kotlin.fir.types.toRegularClassSymbol
import org.jetbrains.kotlin.fir.visibilityChecker
object FirReassignmentAndInvisibleSetterChecker : FirVariableAssignmentChecker() {
override fun check(expression: FirVariableAssignment, context: CheckerContext, reporter: DiagnosticReporter) {
checkInvisibleSetter(expression, context, reporter)
checkValReassignmentViaBackingField(expression, context, reporter)
checkValReassignmentOnValueParameter(expression, context, reporter)
checkVariableExpected(expression, context, reporter)
checkValReassignment(expression, context, reporter)
}
private fun checkInvisibleSetter(
expression: FirVariableAssignment,
context: CheckerContext,
reporter: DiagnosticReporter
) {
fun shouldInvisibleSetterBeReported(symbol: FirPropertySymbol): Boolean {
@OptIn(SymbolInternals::class)
val setterFir = symbol.setterSymbol?.fir ?: symbol.originalForSubstitutionOverride?.setterSymbol?.fir
if (setterFir != null) {
return !context.session.visibilityChecker.isVisible(
setterFir,
context.session,
context.findClosest()!!,
context.containingDeclarations,
expression.dispatchReceiver,
)
}
return false
}
val callableSymbol = expression.calleeReference?.toResolvedCallableSymbol()
if (callableSymbol is FirPropertySymbol && shouldInvisibleSetterBeReported(callableSymbol)) {
val explicitReceiver = expression.explicitReceiver
// Try to get type from smartcast
if (explicitReceiver is FirSmartCastExpression) {
val symbol = explicitReceiver.originalExpression.resolvedType.toRegularClassSymbol(context.session)
if (symbol != null) {
for (declarationSymbol in symbol.declarationSymbols) {
if (declarationSymbol is FirPropertySymbol && declarationSymbol.name == callableSymbol.name) {
if (!shouldInvisibleSetterBeReported(declarationSymbol)) {
return
}
}
}
}
}
reporter.reportOn(
expression.lValue.source,
FirErrors.INVISIBLE_SETTER,
callableSymbol,
callableSymbol.setterSymbol?.visibility ?: Visibilities.Private,
callableSymbol.callableId,
context
)
}
}
private fun checkValReassignmentViaBackingField(
expression: FirVariableAssignment,
context: CheckerContext,
@@ -96,6 +96,12 @@ private fun ConeDiagnostic.toKtDiagnostic(
}
is ConeTypeVisibilityError -> symbol.toInvisibleReferenceDiagnostic(smallestUnresolvablePrefix.last().source)
is ConeSetterVisibilityError -> FirErrors.INVISIBLE_SETTER.createOn(
source,
symbol,
symbol.setterSymbol?.visibility ?: symbol.visibility,
symbol.callableId
)
is ConeVisibilityError -> symbol.toInvisibleReferenceDiagnostic(source)
is ConeInapplicableWrongReceiver -> when (val diagnostic = primaryDiagnostic) {
is DynamicReceiverExpectedButWasNonDynamic ->
@@ -578,11 +578,12 @@ fun createConeDiagnosticForCandidateWithError(
applicability: CandidateApplicability,
candidate: Candidate,
): ConeDiagnostic {
val symbol = candidate.symbol
return when (applicability) {
CandidateApplicability.HIDDEN -> ConeHiddenCandidateError(candidate)
CandidateApplicability.K2_VISIBILITY_ERROR -> {
val session = candidate.callInfo.session
val declaration = candidate.symbol.fir
val declaration = symbol.fir
if (declaration is FirMemberDeclaration &&
session.visibilityChecker.isVisible(declaration, candidate, skipCheckForContainingClassVisibility = true)
) {
@@ -604,13 +605,17 @@ fun createConeDiagnosticForCandidateWithError(
return ConeVisibilityError(it.symbol)
}
}
ConeVisibilityError(candidate.symbol)
if (symbol is FirPropertySymbol && SetterVisibilityError in candidate.diagnostics) {
ConeSetterVisibilityError(symbol)
} else {
ConeVisibilityError(symbol)
}
}
CandidateApplicability.INAPPLICABLE_WRONG_RECEIVER -> ConeInapplicableWrongReceiver(listOf(candidate))
CandidateApplicability.K2_NO_COMPANION_OBJECT -> ConeNoCompanionObject(candidate)
else -> {
if (TypeParameterAsExpression in candidate.diagnostics) {
ConeTypeParameterInQualifiedAccess(candidate.symbol as FirTypeParameterSymbol)
ConeTypeParameterInQualifiedAccess(symbol as FirTypeParameterSymbol)
} else {
ConeInapplicableCandidateError(applicability, candidate)
}
@@ -7,12 +7,10 @@ package org.jetbrains.kotlin.fir.resolve.calls
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.languageVersionSettings
import org.jetbrains.kotlin.fir.matchingParameterFunctionType
import org.jetbrains.kotlin.fir.references.FirSuperReference
import org.jetbrains.kotlin.fir.references.FirThisReference
import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents
@@ -33,7 +31,6 @@ import org.jetbrains.kotlin.fir.symbols.SyntheticSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.visibilityChecker
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.resolve.calls.inference.isSubtypeConstraintCompatible
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind
@@ -611,6 +608,14 @@ internal object CheckVisibility : CheckerStage() {
sink.yieldDiagnostic(VisibilityError)
return
}
if (symbol is FirPropertySymbol && callInfo.callSite is FirVariableAssignment) {
val setterFir = symbol.setterSymbol?.fir ?: symbol.originalForSubstitutionOverride?.setterSymbol?.fir
if (setterFir != null && !visibilityChecker.isVisible(setterFir, candidate)) {
sink.yieldDiagnostic(SetterVisibilityError)
return
}
}
}
if (declaration is FirConstructor) {
@@ -86,6 +86,8 @@ object HiddenCandidate : ResolutionDiagnostic(HIDDEN)
object VisibilityError : ResolutionDiagnostic(K2_VISIBILITY_ERROR)
object SetterVisibilityError : ResolutionDiagnostic(K2_VISIBILITY_ERROR)
object ResolvedWithLowPriority : ResolutionDiagnostic(RESOLVED_LOW_PRIORITY)
object ResolvedWithSynthetic : ResolutionDiagnostic(K2_SYNTHETIC_RESOLVED)
@@ -111,6 +111,12 @@ open class ConeVisibilityError(
override val reason: String get() = "HIDDEN: ${describeSymbol(symbol)} is invisible"
}
open class ConeSetterVisibilityError(
override val symbol: FirPropertySymbol
) : ConeDiagnosticWithSymbol<FirBasedSymbol<*>> {
override val reason: String get() = "HIDDEN SETTER: ${describeSymbol(symbol)} is invisible"
}
class ConeTypeVisibilityError(
symbol: FirBasedSymbol<*>,
val smallestUnresolvablePrefix: List<FirQualifierPart>,
@@ -35,7 +35,7 @@ fun test() {
val u : <!INVISIBLE_REFERENCE!>A<!> = <!INVISIBLE_REFERENCE!>A<!>()
val z = <!INVISIBLE_REFERENCE!>x<!>
<!INVISIBLE_REFERENCE, INVISIBLE_SETTER!>x<!> = 30
<!INVISIBLE_REFERENCE!>x<!> = 30
val po = <!INVISIBLE_REFERENCE!>PO<!>
@@ -26,7 +26,7 @@ package foo
fun test(s: bar.Sub<String>) {
s.<!INVISIBLE_REFERENCE!>name<!>
s.<!INVISIBLE_REFERENCE, INVISIBLE_SETTER!>name<!> = ""
s.<!INVISIBLE_REFERENCE!>name<!> = ""
s.name2
s.name2 = ""
s.<!INVISIBLE_REFERENCE!>doSomething<!>()
+1 -1
View File
@@ -8,6 +8,6 @@ class A {
class B() {
init {
A().<!INVISIBLE_REFERENCE, INVISIBLE_SETTER!>a<!> = "Hello"
A().<!INVISIBLE_REFERENCE!>a<!> = "Hello"
}
}
+1 -1
View File
@@ -6,6 +6,6 @@ class A(private var i: Int, var j: Int) {
}
fun test(a: A) {
a.<!INVISIBLE_REFERENCE, INVISIBLE_SETTER!>i<!>++
a.<!INVISIBLE_REFERENCE!>i<!>++
a.j++
}
@@ -14,7 +14,7 @@ class Derived : Base() {
x.<!INVISIBLE_REFERENCE!>foo<!>()
x.<!INVISIBLE_REFERENCE!>bar<!>()
x.<!INVISIBLE_REFERENCE, INVISIBLE_SETTER!>x<!> = x.<!INVISIBLE_REFERENCE!>x<!> + 1
x.<!INVISIBLE_REFERENCE!>x<!> = x.<!INVISIBLE_REFERENCE!>x<!> + 1
x.<!INVISIBLE_SETTER!>y<!> = x.y + 1
if (x is Derived) {
@@ -54,7 +54,7 @@ open class C : T {
}
fun test4(c: C) {
c.<!INVISIBLE_REFERENCE, INVISIBLE_SETTER!>i<!>++
c.<!INVISIBLE_REFERENCE!>i<!>++
}
class D : C() {
@@ -31,7 +31,7 @@ fun test() {
val u : <!INVISIBLE_REFERENCE!>A<!> = <!INVISIBLE_REFERENCE!>A<!>()
val z = <!INVISIBLE_REFERENCE!>x<!>
<!INVISIBLE_REFERENCE, INVISIBLE_SETTER!>x<!> = 30
<!INVISIBLE_REFERENCE!>x<!> = 30
val po = <!INVISIBLE_REFERENCE!>PO<!>
}
@@ -15,23 +15,23 @@ class Test<in I, out O> {
this.i = getT()
with(Test<I, O>()) {
i = getT() // resolved to this@Test.i
this.<!INVISIBLE_REFERENCE, INVISIBLE_SETTER!>i<!> = getT()
this@with.<!INVISIBLE_REFERENCE, INVISIBLE_SETTER!>i<!> = getT()
this.<!INVISIBLE_REFERENCE!>i<!> = getT()
this@with.<!INVISIBLE_REFERENCE!>i<!> = getT()
this@Test.i = getT()
}
}
fun <I, O> test(t: Test<I, O>) {
t.<!INVISIBLE_REFERENCE, INVISIBLE_SETTER!>i<!> = getT()
t.<!INVISIBLE_REFERENCE!>i<!> = getT()
}
companion object {
fun <I, O> test(t: Test<I, O>) {
t.<!INVISIBLE_REFERENCE, INVISIBLE_SETTER!>i<!> = getT()
t.<!INVISIBLE_REFERENCE!>i<!> = getT()
}
}
}
fun <I, O> test(t: Test<I, O>) {
t.<!INVISIBLE_REFERENCE, INVISIBLE_SETTER!>i<!> = getT()
t.<!INVISIBLE_REFERENCE!>i<!> = getT()
}