FIR checker: don't report errors on type parameter as LHS of class literals
If a type paramter is not reified or nullable, different errors will be reported by FirGetClassCallChecker. When determining whether type parameter wrapped in qualified access is a standalone expression or not, we examine whether the checker context has other qualified accesses in stack. Class literals (::class) is desugared to FirGetClassCall, and thus not stacked as qualified access. Since class literals are a special type of callable reference (a subtype of qualified access), we should keep track of FirGetClassCall in a similar way.
This commit is contained in:
committed by
Mikhail Glukhikh
parent
e29b40b07f
commit
bc2228d434
+23
@@ -11,6 +11,7 @@ import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.persistentSetOf
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||
import org.jetbrains.kotlin.fir.expressions.FirGetClassCall
|
||||
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess
|
||||
import org.jetbrains.kotlin.fir.resolve.ImplicitReceiverStack
|
||||
import org.jetbrains.kotlin.fir.resolve.PersistentImplicitReceiverStack
|
||||
@@ -23,6 +24,7 @@ abstract class CheckerContext {
|
||||
abstract val implicitReceiverStack: ImplicitReceiverStack
|
||||
abstract val containingDeclarations: List<FirDeclaration>
|
||||
abstract val qualifiedAccesses: List<FirQualifiedAccess>
|
||||
abstract val getClassCalls: List<FirGetClassCall>
|
||||
abstract val sessionHolder: SessionHolder
|
||||
abstract val returnTypeCalculator: ReturnTypeCalculator
|
||||
abstract val suppressedDiagnostics: Set<String>
|
||||
@@ -57,6 +59,7 @@ class PersistentCheckerContext private constructor(
|
||||
override val implicitReceiverStack: PersistentImplicitReceiverStack,
|
||||
override val containingDeclarations: PersistentList<FirDeclaration>,
|
||||
override val qualifiedAccesses: PersistentList<FirQualifiedAccess>,
|
||||
override val getClassCalls: PersistentList<FirGetClassCall>,
|
||||
override val sessionHolder: SessionHolder,
|
||||
override val returnTypeCalculator: ReturnTypeCalculator,
|
||||
override val suppressedDiagnostics: PersistentSet<String>,
|
||||
@@ -68,6 +71,7 @@ class PersistentCheckerContext private constructor(
|
||||
PersistentImplicitReceiverStack(),
|
||||
persistentListOf(),
|
||||
persistentListOf(),
|
||||
persistentListOf(),
|
||||
sessionHolder,
|
||||
returnTypeCalculator,
|
||||
persistentSetOf(),
|
||||
@@ -81,6 +85,7 @@ class PersistentCheckerContext private constructor(
|
||||
implicitReceiverStack.add(name, value),
|
||||
containingDeclarations,
|
||||
qualifiedAccesses,
|
||||
getClassCalls,
|
||||
sessionHolder,
|
||||
returnTypeCalculator,
|
||||
suppressedDiagnostics,
|
||||
@@ -95,6 +100,7 @@ class PersistentCheckerContext private constructor(
|
||||
implicitReceiverStack,
|
||||
containingDeclarations.add(declaration),
|
||||
qualifiedAccesses,
|
||||
getClassCalls,
|
||||
sessionHolder,
|
||||
returnTypeCalculator,
|
||||
suppressedDiagnostics,
|
||||
@@ -109,6 +115,22 @@ class PersistentCheckerContext private constructor(
|
||||
implicitReceiverStack,
|
||||
containingDeclarations,
|
||||
qualifiedAccesses.add(qualifiedAccess),
|
||||
getClassCalls,
|
||||
sessionHolder,
|
||||
returnTypeCalculator,
|
||||
suppressedDiagnostics,
|
||||
allInfosSuppressed,
|
||||
allWarningsSuppressed,
|
||||
allErrorsSuppressed
|
||||
)
|
||||
}
|
||||
|
||||
fun addGetClassCall(getClassCall: FirGetClassCall): PersistentCheckerContext {
|
||||
return PersistentCheckerContext(
|
||||
implicitReceiverStack,
|
||||
containingDeclarations,
|
||||
qualifiedAccesses,
|
||||
getClassCalls.add(getClassCall),
|
||||
sessionHolder,
|
||||
returnTypeCalculator,
|
||||
suppressedDiagnostics,
|
||||
@@ -129,6 +151,7 @@ class PersistentCheckerContext private constructor(
|
||||
implicitReceiverStack,
|
||||
containingDeclarations,
|
||||
qualifiedAccesses,
|
||||
getClassCalls,
|
||||
sessionHolder,
|
||||
returnTypeCalculator,
|
||||
suppressedDiagnostics.addAll(diagnosticNames),
|
||||
|
||||
+2
-2
@@ -27,10 +27,10 @@ object FirTypeParameterInQualifiedAccessChecker: FirQualifiedAccessChecker() {
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
// Make sure the current qualified access is not part of another qualified access.
|
||||
// Make sure the current qualified access is not part of another qualified access or class literals.
|
||||
// E.g., for `T::toString`, which is a callable reference (a subtype of qualified access), type parameter T is checked once as an
|
||||
// explicit receiver (or LHS). When we visit `T` (as a qualified access), we should not regard it as an expression here.
|
||||
if (context.qualifiedAccesses.size > 1) return
|
||||
if (context.qualifiedAccesses.size > 1 || context.getClassCalls.isNotEmpty()) return
|
||||
|
||||
val diagnostic = expression.typeRef.coneTypeParameterInQualifiedAccess ?: return
|
||||
val source = expression.source ?: return
|
||||
|
||||
+15
@@ -214,6 +214,10 @@ abstract class AbstractDiagnosticCollector(
|
||||
visitWithQualifiedAccess(qualifiedAccessExpression)
|
||||
}
|
||||
|
||||
override fun visitGetClassCall(getClassCall: FirGetClassCall, data: Nothing?) {
|
||||
visitWithGetClassCall(getClassCall)
|
||||
}
|
||||
|
||||
private inline fun visitWithDeclaration(
|
||||
declaration: FirDeclaration,
|
||||
block: () -> Unit = { declaration.acceptChildren(this, null) }
|
||||
@@ -252,6 +256,17 @@ abstract class AbstractDiagnosticCollector(
|
||||
context = existingContext
|
||||
}
|
||||
}
|
||||
|
||||
private fun visitWithGetClassCall(getClassCall: FirGetClassCall) {
|
||||
val existingContext = context
|
||||
context = context.addGetClassCall(getClassCall)
|
||||
try {
|
||||
getClassCall.runComponents()
|
||||
getClassCall.acceptChildren(this, null)
|
||||
} finally {
|
||||
context = existingContext
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun getDeclarationActionOnDeclarationEnter(declaration: FirDeclaration): DiagnosticCollectorDeclarationAction =
|
||||
|
||||
@@ -9,8 +9,8 @@ val l1 = <!NULLABLE_TYPE_IN_CLASS_LITERAL_LHS!>List<String>?::class<!>
|
||||
val l2 = <!NULLABLE_TYPE_IN_CLASS_LITERAL_LHS!>List?::class<!>
|
||||
|
||||
fun <T : Any> foo() {
|
||||
val t1 = <!TYPE_PARAMETER_AS_REIFIED!><!TYPE_PARAMETER_IS_NOT_AN_EXPRESSION!>T<!>::class<!>
|
||||
val t2 = <!NULLABLE_TYPE_IN_CLASS_LITERAL_LHS!><!TYPE_PARAMETER_IS_NOT_AN_EXPRESSION!>T<!>?::class<!>
|
||||
val t1 = <!TYPE_PARAMETER_AS_REIFIED!>T::class<!>
|
||||
val t2 = <!NULLABLE_TYPE_IN_CLASS_LITERAL_LHS!>T?::class<!>
|
||||
}
|
||||
|
||||
inline fun <reified T : Any> bar() {
|
||||
|
||||
+10
-10
@@ -1,23 +1,23 @@
|
||||
// !DIAGNOSTICS: -TYPE_PARAMETER_AS_REIFIED -TYPE_PARAMETER_OF_PROPERTY_NOT_USED_IN_RECEIVER -UNUSED_VARIABLE -UNUSED_PARAMETER
|
||||
|
||||
fun <T> test1() = <!TYPE_PARAMETER_IS_NOT_AN_EXPRESSION!>T<!>::class
|
||||
fun <T : Any> test2() = <!TYPE_PARAMETER_IS_NOT_AN_EXPRESSION!>T<!>::class
|
||||
fun <T> test1() = T::class
|
||||
fun <T : Any> test2() = T::class
|
||||
|
||||
val <T> test3 = <!TYPE_PARAMETER_IS_NOT_AN_EXPRESSION!>T<!>::class
|
||||
val <T> test4 get() = <!TYPE_PARAMETER_IS_NOT_AN_EXPRESSION!>T<!>::class
|
||||
val <T> test3 = T::class
|
||||
val <T> test4 get() = T::class
|
||||
|
||||
fun <T> test5() = listOf(T::class)
|
||||
|
||||
fun <T> test6(): kotlin.reflect.KClass<T> = <!TYPE_PARAMETER_IS_NOT_AN_EXPRESSION!>T<!>::class
|
||||
fun <T> test7(): kotlin.reflect.KClass<*> = <!TYPE_PARAMETER_IS_NOT_AN_EXPRESSION!>T<!>::class
|
||||
fun <T> test6(): kotlin.reflect.KClass<T> = T::class
|
||||
fun <T> test7(): kotlin.reflect.KClass<*> = T::class
|
||||
fun test8() = <!NULLABLE_TYPE_IN_CLASS_LITERAL_LHS!>String?::class<!>
|
||||
|
||||
fun <T> listOf(e: T): List<T> = null!!
|
||||
|
||||
fun <L> locals() {
|
||||
fun <T> test1() = <!TYPE_PARAMETER_IS_NOT_AN_EXPRESSION!>T<!>::class
|
||||
fun <T : Any> test2() = <!TYPE_PARAMETER_IS_NOT_AN_EXPRESSION!>T<!>::class
|
||||
fun <T> test1() = T::class
|
||||
fun <T : Any> test2() = T::class
|
||||
|
||||
val test3 = <!TYPE_PARAMETER_IS_NOT_AN_EXPRESSION!>L<!>::class
|
||||
fun test4() = <!TYPE_PARAMETER_IS_NOT_AN_EXPRESSION!>L<!>::class
|
||||
val test3 = L::class
|
||||
fun test4() = L::class
|
||||
}
|
||||
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
|
||||
fun <T: Any> dereferenceClass(): Any =
|
||||
<!TYPE_PARAMETER_AS_REIFIED!><!TYPE_PARAMETER_IS_NOT_AN_EXPRESSION!>T<!>::class<!>
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
|
||||
fun <T: Any> dereferenceClass(): Any =
|
||||
<!TYPE_PARAMETER_AS_REIFIED!>T::class<!>
|
||||
|
||||
Reference in New Issue
Block a user