From bc2228d434f434ca790abbf65e7104a2a42c5b1d Mon Sep 17 00:00:00 2001 From: Jinseong Jeon Date: Fri, 5 Mar 2021 00:08:39 -0800 Subject: [PATCH] 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. --- .../checkers/context/CheckerContext.kt | 23 +++++++++++++++++++ ...irTypeParameterInQualifiedAccessChecker.kt | 4 ++-- .../collectors/AbstractDiagnosticCollector.kt | 15 ++++++++++++ .../tests/classLiteral/nonClassesOnLHS.fir.kt | 4 ++-- .../kClassInSignature.fir.kt | 20 ++++++++-------- .../tpAsReified/ClassDereference.fir.kt | 3 --- .../generics/tpAsReified/ClassDereference.kt | 1 + 7 files changed, 53 insertions(+), 17 deletions(-) delete mode 100644 compiler/testData/diagnostics/tests/generics/tpAsReified/ClassDereference.fir.kt diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/context/CheckerContext.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/context/CheckerContext.kt index c4b10689a09..d3b565be268 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/context/CheckerContext.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/context/CheckerContext.kt @@ -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 abstract val qualifiedAccesses: List + abstract val getClassCalls: List abstract val sessionHolder: SessionHolder abstract val returnTypeCalculator: ReturnTypeCalculator abstract val suppressedDiagnostics: Set @@ -57,6 +59,7 @@ class PersistentCheckerContext private constructor( override val implicitReceiverStack: PersistentImplicitReceiverStack, override val containingDeclarations: PersistentList, override val qualifiedAccesses: PersistentList, + override val getClassCalls: PersistentList, override val sessionHolder: SessionHolder, override val returnTypeCalculator: ReturnTypeCalculator, override val suppressedDiagnostics: PersistentSet, @@ -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), diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirTypeParameterInQualifiedAccessChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirTypeParameterInQualifiedAccessChecker.kt index 8666d6026fc..a3f89b03f49 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirTypeParameterInQualifiedAccessChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirTypeParameterInQualifiedAccessChecker.kt @@ -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 diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/AbstractDiagnosticCollector.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/AbstractDiagnosticCollector.kt index 7bf7eca07c9..cbb542804fe 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/AbstractDiagnosticCollector.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/AbstractDiagnosticCollector.kt @@ -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 = diff --git a/compiler/testData/diagnostics/tests/classLiteral/nonClassesOnLHS.fir.kt b/compiler/testData/diagnostics/tests/classLiteral/nonClassesOnLHS.fir.kt index ac672488338..ce57921a22f 100644 --- a/compiler/testData/diagnostics/tests/classLiteral/nonClassesOnLHS.fir.kt +++ b/compiler/testData/diagnostics/tests/classLiteral/nonClassesOnLHS.fir.kt @@ -9,8 +9,8 @@ val l1 = List?::class val l2 = List?::class fun foo() { - val t1 = T::class - val t2 = T?::class + val t1 = T::class + val t2 = T?::class } inline fun bar() { diff --git a/compiler/testData/diagnostics/tests/declarationChecks/kClassInSignature.fir.kt b/compiler/testData/diagnostics/tests/declarationChecks/kClassInSignature.fir.kt index a0692f9641c..8f8337f8b74 100644 --- a/compiler/testData/diagnostics/tests/declarationChecks/kClassInSignature.fir.kt +++ b/compiler/testData/diagnostics/tests/declarationChecks/kClassInSignature.fir.kt @@ -1,23 +1,23 @@ // !DIAGNOSTICS: -TYPE_PARAMETER_AS_REIFIED -TYPE_PARAMETER_OF_PROPERTY_NOT_USED_IN_RECEIVER -UNUSED_VARIABLE -UNUSED_PARAMETER -fun test1() = T::class -fun test2() = T::class +fun test1() = T::class +fun test2() = T::class -val test3 = T::class -val test4 get() = T::class +val test3 = T::class +val test4 get() = T::class fun test5() = listOf(T::class) -fun test6(): kotlin.reflect.KClass = T::class -fun test7(): kotlin.reflect.KClass<*> = T::class +fun test6(): kotlin.reflect.KClass = T::class +fun test7(): kotlin.reflect.KClass<*> = T::class fun test8() = String?::class fun listOf(e: T): List = null!! fun locals() { - fun test1() = T::class - fun test2() = T::class + fun test1() = T::class + fun test2() = T::class - val test3 = L::class - fun test4() = L::class + val test3 = L::class + fun test4() = L::class } diff --git a/compiler/testData/diagnostics/tests/generics/tpAsReified/ClassDereference.fir.kt b/compiler/testData/diagnostics/tests/generics/tpAsReified/ClassDereference.fir.kt deleted file mode 100644 index 64ad9a91d37..00000000000 --- a/compiler/testData/diagnostics/tests/generics/tpAsReified/ClassDereference.fir.kt +++ /dev/null @@ -1,3 +0,0 @@ - -fun dereferenceClass(): Any = - T::class diff --git a/compiler/testData/diagnostics/tests/generics/tpAsReified/ClassDereference.kt b/compiler/testData/diagnostics/tests/generics/tpAsReified/ClassDereference.kt index 7edac44f9b4..9850414f11b 100644 --- a/compiler/testData/diagnostics/tests/generics/tpAsReified/ClassDereference.kt +++ b/compiler/testData/diagnostics/tests/generics/tpAsReified/ClassDereference.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL fun dereferenceClass(): Any = T::class