[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) {