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 ca52822368b..dfbd85b2689 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,14 @@ object FirTypeParameterInQualifiedAccessChecker : FirQualifiedAccessExpressionCh context: CheckerContext, reporter: DiagnosticReporter ) { - // Make sure the current qualified access is not part of another qualified access or class literals. + // Ignore T::class where our expression is T + if (context.getClassCalls.lastOrNull()?.argument == expression) return + + // Make sure the current expression is not the receiver of a qualified access expression. // 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.qualifiedAccessOrAnnotationCalls.size > 1 || context.getClassCalls.isNotEmpty()) return + // explicit receiver. When we visit `T` (as a qualified access expression), we should not regard it as an expression here. + val secondLast = context.qualifiedAccessOrAnnotationCalls.elementAtOrNull(context.qualifiedAccessOrAnnotationCalls.size - 2) + if (secondLast is FirQualifiedAccessExpression && secondLast.explicitReceiver == expression) return val diagnostic = expression.typeRef.coneTypeParameterInQualifiedAccess ?: return val source = expression.source ?: return diff --git a/compiler/testData/diagnostics/tests/resolve/noCandidates/resolvedToClassifier.fir.kt b/compiler/testData/diagnostics/tests/resolve/noCandidates/resolvedToClassifier.fir.kt index 33fc0acf15b..5dbb1db3619 100644 --- a/compiler/testData/diagnostics/tests/resolve/noCandidates/resolvedToClassifier.fir.kt +++ b/compiler/testData/diagnostics/tests/resolve/noCandidates/resolvedToClassifier.fir.kt @@ -16,4 +16,15 @@ fun test() { fun bar() { val typeParameter_as_val = T val typeParameter_as_fun = T() + + baz(T) + baz("$T") + + 1 + T + + B::class.equals(T) + + T = "" } + +fun baz(a: Any) {} diff --git a/compiler/testData/diagnostics/tests/resolve/noCandidates/resolvedToClassifier.kt b/compiler/testData/diagnostics/tests/resolve/noCandidates/resolvedToClassifier.kt index 3890a2547ca..84a6cbae493 100644 --- a/compiler/testData/diagnostics/tests/resolve/noCandidates/resolvedToClassifier.kt +++ b/compiler/testData/diagnostics/tests/resolve/noCandidates/resolvedToClassifier.kt @@ -16,4 +16,15 @@ fun test() { fun bar() { val typeParameter_as_val = T val typeParameter_as_fun = T() + + baz(T) + baz("$T") + + 1 + T + + B::class.equals(T) + + T = "" } + +fun baz(a: Any) {} diff --git a/compiler/testData/diagnostics/tests/resolve/noCandidates/resolvedToClassifier.txt b/compiler/testData/diagnostics/tests/resolve/noCandidates/resolvedToClassifier.txt index c44cf941da7..2acd3669d20 100644 --- a/compiler/testData/diagnostics/tests/resolve/noCandidates/resolvedToClassifier.txt +++ b/compiler/testData/diagnostics/tests/resolve/noCandidates/resolvedToClassifier.txt @@ -1,6 +1,7 @@ package public fun bar(): kotlin.Unit +public fun baz(/*0*/ a: kotlin.Any): kotlin.Unit public fun test(): kotlin.Unit public interface A {