[FIR] Report deprecations in qualified expressions

This commit is contained in:
Andrey Zinovyev
2021-07-16 16:54:06 +03:00
committed by TeamCityServer
parent 98bd6f5d3e
commit 8a0941a85a
32 changed files with 132 additions and 119 deletions
@@ -121,6 +121,7 @@ object CommonExpressionCheckers : ExpressionCheckers() {
get() = setOf(
FirStandaloneQualifierChecker,
FirOptInUsageQualifierChecker,
FirDeprecatedQualifierChecker,
)
override val equalityOperatorCallCheckers: Set<FirEqualityOperatorCallChecker>
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration
import org.jetbrains.kotlin.fir.analysis.checkers.collectEnumEntries
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirDeprecationChecker
import org.jetbrains.kotlin.fir.analysis.checkers.fullyExpandedClass
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
@@ -37,6 +38,7 @@ object FirImportsChecker : FirFileChecker() {
checkOperatorRename(import, context, reporter)
}
}
checkDeprecatedImport(import, context, reporter)
}
checkConflictingImports(declaration.imports, context, reporter)
}
@@ -175,4 +177,12 @@ object FirImportsChecker : FirFileChecker() {
return hasStatic || !hasIllegal
}
private fun checkDeprecatedImport(import: FirImport, context: CheckerContext, reporter: DiagnosticReporter) {
val importedFqName = import.importedFqName ?: return
if (importedFqName.isRoot || importedFqName.shortName().asString().isEmpty()) return
val classId = (import as? FirResolvedImport)?.resolvedClassId ?: ClassId.topLevel(importedFqName)
val classLike: FirRegularClassSymbol = classId.resolveToClass(context) ?: return
FirDeprecationChecker.reportDeprecationIfNeeded(import.source, classLike, null, context, reporter)
}
}
@@ -0,0 +1,24 @@
/*
* 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
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeDeprecated
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
object FirDeprecatedQualifierChecker : FirResolvedQualifierChecker() {
override fun check(expression: FirResolvedQualifier, context: CheckerContext, reporter: DiagnosticReporter) {
expression.nonFatalDiagnostics.filterIsInstance<ConeDeprecated>().forEach { diagnostic ->
FirDeprecationChecker.reportDeprecation(diagnostic.source, diagnostic.symbol, diagnostic.deprecation, reporter, context)
}
if (expression.resolvedToCompanionObject) {
val companionSymbol = (expression.symbol as? FirRegularClassSymbol)?.companionObjectSymbol ?: return
FirDeprecationChecker.reportDeprecationIfNeeded(expression.source, companionSymbol, null, context, reporter)
}
}
}
@@ -17,9 +17,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
import org.jetbrains.kotlin.fir.declarations.Deprecation
import org.jetbrains.kotlin.fir.declarations.DeprecationLevelValue
import org.jetbrains.kotlin.fir.declarations.getDeprecation
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
import org.jetbrains.kotlin.fir.expressions.FirResolvable
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
@@ -50,6 +48,16 @@ object FirDeprecationChecker : FirBasicExpressionChecker() {
reporter: DiagnosticReporter
) {
val deprecation = getWorstDeprecation(callSite, referencedSymbol, context) ?: return
reportDeprecation(source, referencedSymbol, deprecation, reporter, context)
}
internal fun reportDeprecation(
source: FirSourceElement?,
referencedSymbol: FirBasedSymbol<*>,
deprecation: Deprecation,
reporter: DiagnosticReporter,
context: CheckerContext
) {
val diagnostic = when (deprecation.level) {
DeprecationLevelValue.ERROR, DeprecationLevelValue.HIDDEN -> FirErrors.DEPRECATION_ERROR
DeprecationLevelValue.WARNING -> FirErrors.DEPRECATION
@@ -527,36 +527,42 @@ object LightTreePositioningStrategies {
endOffset: Int,
tree: FlyweightCapableTreeStructure<LighterASTNode>
): List<TextRange> {
if (node.tokenType == KtNodeTypes.CALL_EXPRESSION || node.tokenType == KtNodeTypes.CONSTRUCTOR_DELEGATION_CALL) {
return markElement(tree.referenceExpression(node, locateReferencedName) ?: node, startOffset, endOffset, tree, node)
}
if (node.tokenType == KtNodeTypes.PROPERTY_DELEGATE) {
return markElement(tree.findExpressionDeep(node) ?: node, startOffset, endOffset, tree, node)
}
if (node.tokenType == KtNodeTypes.ANNOTATION_ENTRY) {
return markElement(
tree.findDescendantByType(node, KtNodeTypes.CONSTRUCTOR_CALLEE) ?: node,
startOffset,
endOffset,
tree,
node
)
}
if (node.tokenType in nodeTypesWithOperation) {
return markElement(tree.operationReference(node) ?: node, startOffset, endOffset, tree, node)
}
if (node.tokenType == KtNodeTypes.TYPE_REFERENCE) {
val nodeToMark =
tree.findChildByType(node, KtNodeTypes.NULLABLE_TYPE)
?.let { tree.findChildByType(it, KtNodeTypes.USER_TYPE) }
?: node
return markElement(nodeToMark, startOffset, endOffset, tree, node)
}
if (node.tokenType != KtNodeTypes.DOT_QUALIFIED_EXPRESSION &&
node.tokenType != KtNodeTypes.SAFE_ACCESS_EXPRESSION &&
node.tokenType != KtNodeTypes.CALLABLE_REFERENCE_EXPRESSION
) {
return super.mark(node, startOffset, endOffset, tree)
when {
node.tokenType == KtNodeTypes.CALL_EXPRESSION || node.tokenType == KtNodeTypes.CONSTRUCTOR_DELEGATION_CALL -> {
return markElement(tree.referenceExpression(node, locateReferencedName) ?: node, startOffset, endOffset, tree, node)
}
node.tokenType == KtNodeTypes.PROPERTY_DELEGATE -> {
return markElement(tree.findExpressionDeep(node) ?: node, startOffset, endOffset, tree, node)
}
node.tokenType == KtNodeTypes.ANNOTATION_ENTRY -> {
return markElement(
tree.findDescendantByType(node, KtNodeTypes.CONSTRUCTOR_CALLEE) ?: node,
startOffset,
endOffset,
tree,
node
)
}
node.tokenType in nodeTypesWithOperation -> {
return markElement(tree.operationReference(node) ?: node, startOffset, endOffset, tree, node)
}
node.tokenType == KtNodeTypes.TYPE_REFERENCE -> {
val nodeToMark =
tree.findChildByType(node, KtNodeTypes.NULLABLE_TYPE)
?.let { tree.findChildByType(it, KtNodeTypes.USER_TYPE) }
?: node
return markElement(nodeToMark, startOffset, endOffset, tree, node)
}
node.tokenType == KtNodeTypes.IMPORT_DIRECTIVE -> {
val nodeToMark = tree.findChildByType(node, KtStubElementTypes.INSIDE_DIRECTIVE_EXPRESSIONS) ?: node
return markElement(nodeToMark, startOffset, endOffset, tree, node)
}
node.tokenType != KtNodeTypes.DOT_QUALIFIED_EXPRESSION &&
node.tokenType != KtNodeTypes.SAFE_ACCESS_EXPRESSION &&
node.tokenType != KtNodeTypes.CALLABLE_REFERENCE_EXPRESSION
-> {
return super.mark(node, startOffset, endOffset, tree)
}
}
val selector = tree.selector(node)
if (selector != null) {
@@ -254,7 +254,12 @@ class FirCallResolver(
referencedSymbol,
nameReference.source,
qualifiedAccess.typeArguments,
diagnostic
diagnostic,
nonFatalDiagnostics = extractNonFatalDiagnostics(
nameReference.source,
qualifiedAccess.explicitReceiver,
referencedSymbol
)
)
}
referencedSymbol is FirTypeParameterSymbol && referencedSymbol.fir.isReified -> {
@@ -276,6 +281,17 @@ class FirCallResolver(
return resultExpression
}
private fun extractNonFatalDiagnostics(
source: FirSourceElement?,
explicitReceiver: FirExpression?,
symbol: FirClassLikeSymbol<*>
): List<ConeDiagnostic> {
val prevDiagnostics = (explicitReceiver as? FirResolvedQualifier)?.nonFatalDiagnostics ?: emptyList()
return symbol.fir.deprecation?.forUseSite()?.let {
prevDiagnostics + ConeDeprecated(source, symbol, it)
} ?: prevDiagnostics
}
fun resolveCallableReference(
constraintSystemBuilder: ConstraintSystemBuilder,
resolvedCallableReferenceAtom: ResolvedCallableReferenceAtom,
@@ -114,7 +114,8 @@ fun BodyResolveComponents.buildResolvedQualifierForClass(
sourceElement: FirSourceElement? = null,
// TODO: Clarify if we actually need type arguments for qualifier?
typeArgumentsForQualifier: List<FirTypeProjection> = emptyList(),
diagnostic: ConeDiagnostic? = null
diagnostic: ConeDiagnostic? = null,
nonFatalDiagnostics: List<ConeDiagnostic> = emptyList()
): FirResolvedQualifier {
val classId = regularClass.classId
@@ -130,6 +131,7 @@ fun BodyResolveComponents.buildResolvedQualifierForClass(
relativeClassFqName = classId.relativeClassName
typeArguments.addAll(typeArgumentsForQualifier)
symbol = regularClass
this.nonFatalDiagnostics.addAll(nonFatalDiagnostics)
}.build().apply {
resultType = if (classId.isLocal) {
typeForQualifierByDeclaration(regularClass.fir, resultType, session)
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.resolve.diagnostics
import kotlinx.collections.immutable.ImmutableList
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.declarations.Deprecation
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
@@ -147,6 +148,10 @@ class ConeUnsupportedDynamicType() : ConeDiagnostic() {
override val reason: String get() = "Dynamic types are not supported in this context"
}
class ConeDeprecated(val source: FirSourceElement?, val symbol: FirBasedSymbol<*>, val deprecation: Deprecation) : ConeDiagnostic() {
override val reason: String get() = "Deprecated: ${deprecation.message}"
}
private fun describeSymbol(symbol: FirBasedSymbol<*>): String {
return when (symbol) {
is FirClassLikeSymbol<*> -> symbol.classId.asString()
@@ -31,6 +31,7 @@ abstract class FirErrorResolvedQualifier : FirResolvedQualifier(), FirDiagnostic
abstract override val symbol: FirClassLikeSymbol<*>?
abstract override val isNullableLHSForCallableReference: Boolean
abstract override val resolvedToCompanionObject: Boolean
abstract override val nonFatalDiagnostics: List<ConeDiagnostic>
abstract override val typeArguments: List<FirTypeProjection>
abstract override val diagnostic: ConeDiagnostic
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.expressions
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.types.FirTypeProjection
import org.jetbrains.kotlin.fir.types.FirTypeRef
@@ -29,6 +30,7 @@ abstract class FirResolvedQualifier : FirExpression() {
abstract val symbol: FirClassLikeSymbol<*>?
abstract val isNullableLHSForCallableReference: Boolean
abstract val resolvedToCompanionObject: Boolean
abstract val nonFatalDiagnostics: List<ConeDiagnostic>
abstract val typeArguments: List<FirTypeProjection>
override fun <R, D> accept(visitor: FirVisitor<R, D>, data: D): R = visitor.visitResolvedQualifier(this, data)
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.expressions.builder
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.builder.FirBuilderDsl
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
@@ -32,6 +33,7 @@ interface FirAbstractResolvedQualifierBuilder {
abstract var symbol: FirClassLikeSymbol<*>?
abstract var isNullableLHSForCallableReference: Boolean
abstract var resolvedToCompanionObject: Boolean
abstract val nonFatalDiagnostics: MutableList<ConeDiagnostic>
abstract val typeArguments: MutableList<FirTypeProjection>
fun build(): FirResolvedQualifier
@@ -37,6 +37,7 @@ class FirErrorResolvedQualifierBuilder : FirAbstractResolvedQualifierBuilder, Fi
override var classId: ClassId? = null
override var symbol: FirClassLikeSymbol<*>? = null
override var isNullableLHSForCallableReference: Boolean = false
override val nonFatalDiagnostics: MutableList<ConeDiagnostic> = mutableListOf()
override val typeArguments: MutableList<FirTypeProjection> = mutableListOf()
lateinit var diagnostic: ConeDiagnostic
@@ -49,6 +50,7 @@ class FirErrorResolvedQualifierBuilder : FirAbstractResolvedQualifierBuilder, Fi
classId,
symbol,
isNullableLHSForCallableReference,
nonFatalDiagnostics,
typeArguments,
diagnostic,
)
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.builder.FirAnnotationContainerBuilder
import org.jetbrains.kotlin.fir.builder.FirBuilderDsl
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
import org.jetbrains.kotlin.fir.expressions.builder.FirAbstractResolvedQualifierBuilder
@@ -37,6 +38,7 @@ class FirResolvedQualifierBuilder : FirAbstractResolvedQualifierBuilder, FirAnno
override var relativeClassFqName: FqName? = null
override var symbol: FirClassLikeSymbol<*>? = null
override var isNullableLHSForCallableReference: Boolean = false
override val nonFatalDiagnostics: MutableList<ConeDiagnostic> = mutableListOf()
override val typeArguments: MutableList<FirTypeProjection> = mutableListOf()
override fun build(): FirResolvedQualifier {
@@ -48,6 +50,7 @@ class FirResolvedQualifierBuilder : FirAbstractResolvedQualifierBuilder, FirAnno
relativeClassFqName,
symbol,
isNullableLHSForCallableReference,
nonFatalDiagnostics,
typeArguments,
)
}
@@ -30,6 +30,7 @@ internal class FirErrorResolvedQualifierImpl(
override val classId: ClassId?,
override val symbol: FirClassLikeSymbol<*>?,
override var isNullableLHSForCallableReference: Boolean,
override val nonFatalDiagnostics: MutableList<ConeDiagnostic>,
override val typeArguments: MutableList<FirTypeProjection>,
override val diagnostic: ConeDiagnostic,
) : FirErrorResolvedQualifier() {
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.expressions.impl
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
@@ -29,6 +30,7 @@ internal class FirResolvedQualifierImpl(
override var relativeClassFqName: FqName?,
override val symbol: FirClassLikeSymbol<*>?,
override var isNullableLHSForCallableReference: Boolean,
override val nonFatalDiagnostics: MutableList<ConeDiagnostic>,
override val typeArguments: MutableList<FirTypeProjection>,
) : FirResolvedQualifier() {
override val classId: ClassId? get() = relativeClassFqName?.let {
@@ -503,6 +503,7 @@ object NodeConfigurator : AbstractFieldConfigurator<FirTreeBuilder>(FirTreeBuild
+field("symbol", classLikeSymbolType, nullable = true)
+booleanField("isNullableLHSForCallableReference", withReplace = true)
+booleanField("resolvedToCompanionObject", withReplace = true)
+fieldList("nonFatalDiagnostics", coneDiagnosticType)
+typeArguments.withTransform()
}