[FIR] Split UpperBoundViolated checkers on two files (expression and declaration), add FirUpperBoundViolatedHelpers

This commit is contained in:
Ivan Kochurkin
2021-06-25 18:58:08 +03:00
committed by teamcityserver
parent a26ffde820
commit 568eb255f5
4 changed files with 109 additions and 87 deletions
@@ -15,7 +15,6 @@ import org.jetbrains.kotlin.fir.analysis.checkers.syntax.FirDelegationInInterfac
import org.jetbrains.kotlin.fir.analysis.checkers.syntax.FirFunctionTypeParametersSyntaxChecker
import org.jetbrains.kotlin.fir.analysis.checkers.syntax.FirTypeParameterSyntaxChecker
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirReservedUnderscoreDeclarationChecker
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirUpperBoundViolatedClassChecker
object CommonDeclarationCheckers : DeclarationCheckers() {
override val basicDeclarationCheckers: Set<FirBasicDeclarationChecker>
@@ -25,7 +24,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
FirProjectionRelationChecker,
FirTypeConstraintsChecker,
FirReservedUnderscoreDeclarationChecker,
FirUpperBoundViolatedClassChecker
FirUpperBoundViolatedDeclarationChecker
)
override val memberDeclarationCheckers: Set<FirMemberDeclarationChecker>
@@ -1,109 +1,29 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.expression
package org.jetbrains.kotlin.fir.analysis.checkers
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.analysis.checkers.FirTypeRefSource
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker
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.*
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeInapplicableCandidateError
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.fir.typeContext
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
object FirUpperBoundViolatedClassChecker : FirBasicDeclarationChecker() {
override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration is FirClass<*>) {
for (typeParameter in declaration.typeParameters) {
if (typeParameter is FirTypeParameter) {
for (bound in typeParameter.bounds) {
checkUpperBoundViolated(bound, context, reporter)
}
}
}
for (superTypeRef in declaration.superTypeRefs) {
checkUpperBoundViolated(superTypeRef, context, reporter)
}
} else if (declaration is FirTypeAlias) {
checkUpperBoundViolated(declaration.expandedTypeRef, context, reporter, isIgnoreTypeParameters = true)
} else if (declaration is FirCallableDeclaration<*>) {
if (declaration.returnTypeRef.source?.kind !is FirFakeSourceElementKind) {
checkUpperBoundViolated(
declaration.returnTypeRef, context, reporter,
isIgnoreTypeParameters = context.containingDeclarations.lastOrNull() is FirTypeAlias
)
}
}
}
}
object FirUpperBoundViolatedExpressionChecker : FirQualifiedAccessExpressionChecker() {
override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) {
// something that contains the type parameters
// declarations with their declared bounds.
// it may be the called function declaration
// or the class declaration
val calleReference = expression.calleeReference
var calleeFir: FirTypeParameterRefsOwner? = null
if (calleReference is FirResolvedNamedReference) {
calleeFir = calleReference.safeAs<FirResolvedNamedReference>()?.resolvedSymbol?.fir.safeAs()
} else if (calleReference is FirErrorNamedReference) {
val diagnostic = calleReference.diagnostic
if (diagnostic is ConeInapplicableCandidateError &&
diagnostic.applicability == CandidateApplicability.INAPPLICABLE_WRONG_RECEIVER
) {
return
}
calleeFir = calleReference.candidateSymbol?.fir.safeAs()
}
var typeArguments: List<Any>? = null
var typeArgumentRefsAndSources: List<FirTypeRefSource?>? = null
val typeParameters = if (calleeFir is FirConstructor) {
typeArgumentRefsAndSources =
expression.typeArguments.map { FirTypeRefSource((it as? FirTypeProjectionWithVariance)?.typeRef, it.source) }
val prototypeClass = calleeFir.dispatchReceiverType?.toSymbol(context.session)?.fir.safeAs<FirRegularClass>()
prototypeClass?.typeParameters?.map { it.symbol }
} else {
typeArguments = expression.typeArguments
calleeFir?.typeParameters?.map { it.symbol }
}
checkUpperBoundViolated(
expression.typeRef,
context,
reporter,
typeParameters,
typeArguments,
typeArgumentRefsAndSources
)
}
}
/**
* Recursively analyzes type parameters and reports the diagnostic on the given source calculated using typeRef
* Returns true if an error occurred
*/
private fun checkUpperBoundViolated(
fun checkUpperBoundViolated(
typeRef: FirTypeRef?,
context: CheckerContext,
reporter: DiagnosticReporter,
@@ -128,7 +48,7 @@ private fun checkUpperBoundViolated(
?: if (type is ConeClassLikeType) {
val fullyExpandedType = type.fullyExpandedType(context.session)
val prototypeClass = fullyExpandedType.lookupTag.toSymbol(context.session)
?.fir.safeAs<FirRegularClass>()
?.fir as? FirRegularClass
?: return
if (type != fullyExpandedType) {
@@ -0,0 +1,39 @@
/*
* 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.FirFakeSourceElementKind
import org.jetbrains.kotlin.fir.analysis.checkers.checkUpperBoundViolated
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.declarations.*
object FirUpperBoundViolatedDeclarationChecker : FirBasicDeclarationChecker() {
override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration is FirClass<*>) {
for (typeParameter in declaration.typeParameters) {
if (typeParameter is FirTypeParameter) {
for (bound in typeParameter.bounds) {
checkUpperBoundViolated(bound, context, reporter)
}
}
}
for (superTypeRef in declaration.superTypeRefs) {
checkUpperBoundViolated(superTypeRef, context, reporter)
}
} else if (declaration is FirTypeAlias) {
checkUpperBoundViolated(declaration.expandedTypeRef, context, reporter, isIgnoreTypeParameters = true)
} else if (declaration is FirCallableDeclaration<*>) {
if (declaration.returnTypeRef.source?.kind !is FirFakeSourceElementKind) {
checkUpperBoundViolated(
declaration.returnTypeRef, context, reporter,
isIgnoreTypeParameters = context.containingDeclarations.lastOrNull() is FirTypeAlias
)
}
}
}
}
@@ -0,0 +1,64 @@
/*
* Copyright 2010-2020 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.expression
import org.jetbrains.kotlin.fir.analysis.checkers.FirTypeRefSource
import org.jetbrains.kotlin.fir.analysis.checkers.checkUpperBoundViolated
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.declarations.FirConstructor
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.FirTypeParameterRefsOwner
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeInapplicableCandidateError
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
object FirUpperBoundViolatedExpressionChecker : FirQualifiedAccessExpressionChecker() {
override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) {
// something that contains the type parameters
// declarations with their declared bounds.
// it may be the called function declaration
// or the class declaration
val calleReference = expression.calleeReference
var calleeFir: FirTypeParameterRefsOwner? = null
if (calleReference is FirResolvedNamedReference) {
calleeFir = calleReference.safeAs<FirResolvedNamedReference>()?.resolvedSymbol?.fir.safeAs()
} else if (calleReference is FirErrorNamedReference) {
val diagnostic = calleReference.diagnostic
if (diagnostic is ConeInapplicableCandidateError &&
diagnostic.applicability == CandidateApplicability.INAPPLICABLE_WRONG_RECEIVER
) {
return
}
calleeFir = calleReference.candidateSymbol?.fir.safeAs()
}
var typeArguments: List<Any>? = null
var typeArgumentRefsAndSources: List<FirTypeRefSource?>? = null
val typeParameters = if (calleeFir is FirConstructor) {
typeArgumentRefsAndSources =
expression.typeArguments.map { FirTypeRefSource((it as? FirTypeProjectionWithVariance)?.typeRef, it.source) }
val prototypeClass = calleeFir.dispatchReceiverType?.toSymbol(context.session)?.fir.safeAs<FirRegularClass>()
prototypeClass?.typeParameters?.map { it.symbol }
} else {
typeArguments = expression.typeArguments
calleeFir?.typeParameters?.map { it.symbol }
}
checkUpperBoundViolated(
expression.typeRef,
context,
reporter,
typeParameters,
typeArguments,
typeArgumentRefsAndSources
)
}
}