[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| { 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 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.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.cfa.evaluatedInPlace 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.checkers.getContainingSymbol
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.declarations.* 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.ConeSimpleDiagnostic
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.originalForSubstitutionOverride
import org.jetbrains.kotlin.fir.references.* import org.jetbrains.kotlin.fir.references.*
import org.jetbrains.kotlin.fir.resolve.dfa.controlFlowGraph import org.jetbrains.kotlin.fir.resolve.dfa.controlFlowGraph
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeDiagnosticWithCandidates import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeDiagnosticWithCandidates
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedNameError 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.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() { object FirReassignmentAndInvisibleSetterChecker : FirVariableAssignmentChecker() {
override fun check(expression: FirVariableAssignment, context: CheckerContext, reporter: DiagnosticReporter) { override fun check(expression: FirVariableAssignment, context: CheckerContext, reporter: DiagnosticReporter) {
checkInvisibleSetter(expression, context, reporter)
checkValReassignmentViaBackingField(expression, context, reporter) checkValReassignmentViaBackingField(expression, context, reporter)
checkValReassignmentOnValueParameter(expression, context, reporter) checkValReassignmentOnValueParameter(expression, context, reporter)
checkVariableExpected(expression, context, reporter) checkVariableExpected(expression, context, reporter)
checkValReassignment(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( private fun checkValReassignmentViaBackingField(
expression: FirVariableAssignment, expression: FirVariableAssignment,
context: CheckerContext, context: CheckerContext,
@@ -96,6 +96,12 @@ private fun ConeDiagnostic.toKtDiagnostic(
} }
is ConeTypeVisibilityError -> symbol.toInvisibleReferenceDiagnostic(smallestUnresolvablePrefix.last().source) 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 ConeVisibilityError -> symbol.toInvisibleReferenceDiagnostic(source)
is ConeInapplicableWrongReceiver -> when (val diagnostic = primaryDiagnostic) { is ConeInapplicableWrongReceiver -> when (val diagnostic = primaryDiagnostic) {
is DynamicReceiverExpectedButWasNonDynamic -> is DynamicReceiverExpectedButWasNonDynamic ->
@@ -578,11 +578,12 @@ fun createConeDiagnosticForCandidateWithError(
applicability: CandidateApplicability, applicability: CandidateApplicability,
candidate: Candidate, candidate: Candidate,
): ConeDiagnostic { ): ConeDiagnostic {
val symbol = candidate.symbol
return when (applicability) { return when (applicability) {
CandidateApplicability.HIDDEN -> ConeHiddenCandidateError(candidate) CandidateApplicability.HIDDEN -> ConeHiddenCandidateError(candidate)
CandidateApplicability.K2_VISIBILITY_ERROR -> { CandidateApplicability.K2_VISIBILITY_ERROR -> {
val session = candidate.callInfo.session val session = candidate.callInfo.session
val declaration = candidate.symbol.fir val declaration = symbol.fir
if (declaration is FirMemberDeclaration && if (declaration is FirMemberDeclaration &&
session.visibilityChecker.isVisible(declaration, candidate, skipCheckForContainingClassVisibility = true) session.visibilityChecker.isVisible(declaration, candidate, skipCheckForContainingClassVisibility = true)
) { ) {
@@ -604,13 +605,17 @@ fun createConeDiagnosticForCandidateWithError(
return ConeVisibilityError(it.symbol) 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.INAPPLICABLE_WRONG_RECEIVER -> ConeInapplicableWrongReceiver(listOf(candidate))
CandidateApplicability.K2_NO_COMPANION_OBJECT -> ConeNoCompanionObject(candidate) CandidateApplicability.K2_NO_COMPANION_OBJECT -> ConeNoCompanionObject(candidate)
else -> { else -> {
if (TypeParameterAsExpression in candidate.diagnostics) { if (TypeParameterAsExpression in candidate.diagnostics) {
ConeTypeParameterInQualifiedAccess(candidate.symbol as FirTypeParameterSymbol) ConeTypeParameterInQualifiedAccess(symbol as FirTypeParameterSymbol)
} else { } else {
ConeInapplicableCandidateError(applicability, candidate) ConeInapplicableCandidateError(applicability, candidate)
} }
@@ -7,12 +7,10 @@ package org.jetbrains.kotlin.fir.resolve.calls
import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.Modality 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.*
import org.jetbrains.kotlin.fir.declarations.utils.* import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.expressions.* 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.FirSuperReference
import org.jetbrains.kotlin.fir.references.FirThisReference import org.jetbrains.kotlin.fir.references.FirThisReference
import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents 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.impl.*
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.visibilityChecker
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.resolve.calls.inference.isSubtypeConstraintCompatible import org.jetbrains.kotlin.resolve.calls.inference.isSubtypeConstraintCompatible
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind
@@ -611,6 +608,14 @@ internal object CheckVisibility : CheckerStage() {
sink.yieldDiagnostic(VisibilityError) sink.yieldDiagnostic(VisibilityError)
return 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) { if (declaration is FirConstructor) {
@@ -86,6 +86,8 @@ object HiddenCandidate : ResolutionDiagnostic(HIDDEN)
object VisibilityError : ResolutionDiagnostic(K2_VISIBILITY_ERROR) object VisibilityError : ResolutionDiagnostic(K2_VISIBILITY_ERROR)
object SetterVisibilityError : ResolutionDiagnostic(K2_VISIBILITY_ERROR)
object ResolvedWithLowPriority : ResolutionDiagnostic(RESOLVED_LOW_PRIORITY) object ResolvedWithLowPriority : ResolutionDiagnostic(RESOLVED_LOW_PRIORITY)
object ResolvedWithSynthetic : ResolutionDiagnostic(K2_SYNTHETIC_RESOLVED) object ResolvedWithSynthetic : ResolutionDiagnostic(K2_SYNTHETIC_RESOLVED)
@@ -111,6 +111,12 @@ open class ConeVisibilityError(
override val reason: String get() = "HIDDEN: ${describeSymbol(symbol)} is invisible" 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( class ConeTypeVisibilityError(
symbol: FirBasedSymbol<*>, symbol: FirBasedSymbol<*>,
val smallestUnresolvablePrefix: List<FirQualifierPart>, val smallestUnresolvablePrefix: List<FirQualifierPart>,
@@ -35,7 +35,7 @@ fun test() {
val u : <!INVISIBLE_REFERENCE!>A<!> = <!INVISIBLE_REFERENCE!>A<!>() val u : <!INVISIBLE_REFERENCE!>A<!> = <!INVISIBLE_REFERENCE!>A<!>()
val z = <!INVISIBLE_REFERENCE!>x<!> val z = <!INVISIBLE_REFERENCE!>x<!>
<!INVISIBLE_REFERENCE, INVISIBLE_SETTER!>x<!> = 30 <!INVISIBLE_REFERENCE!>x<!> = 30
val po = <!INVISIBLE_REFERENCE!>PO<!> val po = <!INVISIBLE_REFERENCE!>PO<!>
@@ -26,7 +26,7 @@ package foo
fun test(s: bar.Sub<String>) { fun test(s: bar.Sub<String>) {
s.<!INVISIBLE_REFERENCE!>name<!> s.<!INVISIBLE_REFERENCE!>name<!>
s.<!INVISIBLE_REFERENCE, INVISIBLE_SETTER!>name<!> = "" s.<!INVISIBLE_REFERENCE!>name<!> = ""
s.name2 s.name2
s.name2 = "" s.name2 = ""
s.<!INVISIBLE_REFERENCE!>doSomething<!>() s.<!INVISIBLE_REFERENCE!>doSomething<!>()
+1 -1
View File
@@ -8,6 +8,6 @@ class A {
class B() { class B() {
init { 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) { fun test(a: A) {
a.<!INVISIBLE_REFERENCE, INVISIBLE_SETTER!>i<!>++ a.<!INVISIBLE_REFERENCE!>i<!>++
a.j++ a.j++
} }
@@ -14,7 +14,7 @@ class Derived : Base() {
x.<!INVISIBLE_REFERENCE!>foo<!>() x.<!INVISIBLE_REFERENCE!>foo<!>()
x.<!INVISIBLE_REFERENCE!>bar<!>() 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 x.<!INVISIBLE_SETTER!>y<!> = x.y + 1
if (x is Derived) { if (x is Derived) {
@@ -54,7 +54,7 @@ open class C : T {
} }
fun test4(c: C) { fun test4(c: C) {
c.<!INVISIBLE_REFERENCE, INVISIBLE_SETTER!>i<!>++ c.<!INVISIBLE_REFERENCE!>i<!>++
} }
class D : C() { class D : C() {
@@ -31,7 +31,7 @@ fun test() {
val u : <!INVISIBLE_REFERENCE!>A<!> = <!INVISIBLE_REFERENCE!>A<!>() val u : <!INVISIBLE_REFERENCE!>A<!> = <!INVISIBLE_REFERENCE!>A<!>()
val z = <!INVISIBLE_REFERENCE!>x<!> val z = <!INVISIBLE_REFERENCE!>x<!>
<!INVISIBLE_REFERENCE, INVISIBLE_SETTER!>x<!> = 30 <!INVISIBLE_REFERENCE!>x<!> = 30
val po = <!INVISIBLE_REFERENCE!>PO<!> val po = <!INVISIBLE_REFERENCE!>PO<!>
} }
@@ -15,23 +15,23 @@ class Test<in I, out O> {
this.i = getT() this.i = getT()
with(Test<I, O>()) { with(Test<I, O>()) {
i = getT() // resolved to this@Test.i i = getT() // resolved to this@Test.i
this.<!INVISIBLE_REFERENCE, INVISIBLE_SETTER!>i<!> = getT() this.<!INVISIBLE_REFERENCE!>i<!> = getT()
this@with.<!INVISIBLE_REFERENCE, INVISIBLE_SETTER!>i<!> = getT() this@with.<!INVISIBLE_REFERENCE!>i<!> = getT()
this@Test.i = getT() this@Test.i = getT()
} }
} }
fun <I, O> test(t: Test<I, O>) { fun <I, O> test(t: Test<I, O>) {
t.<!INVISIBLE_REFERENCE, INVISIBLE_SETTER!>i<!> = getT() t.<!INVISIBLE_REFERENCE!>i<!> = getT()
} }
companion object { companion object {
fun <I, O> test(t: Test<I, O>) { 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>) { fun <I, O> test(t: Test<I, O>) {
t.<!INVISIBLE_REFERENCE, INVISIBLE_SETTER!>i<!> = getT() t.<!INVISIBLE_REFERENCE!>i<!> = getT()
} }