[FIR] Check rest OUTER_CLASS_ARGUMENTS_REQUIRED diagnostics on checkers stage

Add FirOuterClassArgumentsRequiredChecker
This commit is contained in:
Ivan Kochurkin
2021-07-06 01:24:53 +03:00
committed by TeamCityServer
parent 0fc8be4d60
commit 28a6928873
5 changed files with 121 additions and 7 deletions
@@ -88,6 +88,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
FirFunInterfaceDeclarationChecker,
FirNestedClassChecker,
FirInlineClassDeclarationChecker,
FirOuterClassArgumentsRequiredChecker
)
override val constructorCheckers: Set<FirConstructorChecker>
@@ -0,0 +1,109 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.analysis.checkers.declaration
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.context.findClosest
import org.jetbrains.kotlin.fir.analysis.checkers.extractArgumentTypeRefAndSource
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.FirTypeParameterRef
import org.jetbrains.kotlin.fir.declarations.FirTypedDeclaration
import org.jetbrains.kotlin.fir.resolve.getClassThatContainsTypeParameter
import org.jetbrains.kotlin.fir.resolve.isValidTypeParameter
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.resolve.toTypeProjections
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.types.*
object FirOuterClassArgumentsRequiredChecker : FirRegularClassChecker() {
override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) {
for (superTypeRef in declaration.superTypeRefs) {
checkOuterClassArgumentsRequired(superTypeRef, declaration, context, reporter)
}
for (subDecl in declaration.declarations) {
if (subDecl is FirTypedDeclaration) {
checkOuterClassArgumentsRequired(subDecl.returnTypeRef, declaration, context, reporter)
}
}
}
}
private fun checkOuterClassArgumentsRequired(
typeRef: FirTypeRef,
declaration: FirRegularClass?,
context: CheckerContext,
reporter: DiagnosticReporter
) {
val type: ConeKotlinType
if (typeRef is FirResolvedTypeRef) {
if (typeRef is FirErrorTypeRef) {
return
}
type = typeRef.type
val delegatedTypeRef = typeRef.delegatedTypeRef
if (delegatedTypeRef is FirUserTypeRef) {
if (type is ConeClassLikeType) {
val symbol = type.lookupTag.toSymbol(context.session)
if (symbol is FirRegularClassSymbol) {
var problemTypeParameter: FirTypeParameterRef? = null
val typeArguments = delegatedTypeRef.qualifier.toTypeProjections()
val argumentsFromOuterClassesAndParentsCount = symbol.fir.typeParameters.drop(typeArguments.size).sumOf {
val result = if (isValidTypeParameter(it, declaration, context.session)) {
1
} else {
if (problemTypeParameter == null) {
problemTypeParameter = it
}
0
}
return@sumOf result
}
val finalTypeArgumentsCount = typeArguments.size + argumentsFromOuterClassesAndParentsCount
if (finalTypeArgumentsCount != symbol.fir.typeParameters.size) {
val source = typeRef.source
if (problemTypeParameter != null) {
var outerClass: FirRegularClass? = null
context.findClosest<FirRegularClass> {
outerClass = getClassThatContainsTypeParameter(it, problemTypeParameter!!)
return@findClosest outerClass != null
}
if (outerClass != null) {
reporter.reportOn(source, FirErrors.OUTER_CLASS_ARGUMENTS_REQUIRED, outerClass!!, context)
}
} else {
reporter.reportOn(
source,
FirErrors.WRONG_NUMBER_OF_TYPE_ARGUMENTS,
symbol.fir.typeParameters.size - argumentsFromOuterClassesAndParentsCount,
symbol,
context
)
}
}
}
}
}
} else if (typeRef is ConeKotlinType) {
type = typeRef
} else {
return
}
for (index in type.typeArguments.indices) {
val firTypeRefSource = extractArgumentTypeRefAndSource(typeRef, index) ?: continue
firTypeRefSource.typeRef?.let { checkOuterClassArgumentsRequired(it, declaration, context, reporter) }
}
}
@@ -1,7 +0,0 @@
interface Inv<X>
class Outer<E> {
inner class Inner
class Nested : Inv<Inner>
object Obj : Inv<Inner>
}
@@ -1,7 +1,10 @@
// FIR_IDENTICAL
interface Inv<X>
class Outer<E> {
inner class Inner
class Nested : Inv<<!OUTER_CLASS_ARGUMENTS_REQUIRED!>Inner<!>>
inner class Inner2 : Inv<Inner> // no error
object Obj : Inv<<!OUTER_CLASS_ARGUMENTS_REQUIRED!>Inner<!>>
}
@@ -19,6 +19,13 @@ public final class Outer</*0*/ E> {
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final inner class Inner2 /*captured type parameters: /*0*/ E*/ : Inv<Outer<E>.Inner> {
public constructor Inner2()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class Nested : Inv<[ERROR : Inner]> {
public constructor Nested()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
@@ -33,3 +40,4 @@ public final class Outer</*0*/ E> {
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}