[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:
committed by
Space Team
parent
737b286d5c
commit
5186ba80e2
+1
-1
@@ -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)
|
||||
}
|
||||
|
||||
-56
@@ -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,
|
||||
|
||||
+6
@@ -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) {
|
||||
|
||||
+2
@@ -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)
|
||||
|
||||
+6
@@ -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<!>
|
||||
|
||||
|
||||
+1
-1
@@ -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<!>()
|
||||
|
||||
@@ -8,6 +8,6 @@ class A {
|
||||
|
||||
class B() {
|
||||
init {
|
||||
A().<!INVISIBLE_REFERENCE, INVISIBLE_SETTER!>a<!> = "Hello"
|
||||
A().<!INVISIBLE_REFERENCE!>a<!> = "Hello"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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++
|
||||
}
|
||||
|
||||
+1
-1
@@ -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()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user