[FIR] Add REDUNDANT_NULLABLE diagnostics

This commit is contained in:
Ivan Kochurkin
2021-11-19 16:21:51 +03:00
committed by TeamCityServer
parent 4caf3c5e83
commit 51b73bb6ae
37 changed files with 233 additions and 87 deletions
@@ -655,6 +655,8 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
parameter<String>("description")
parameter<Boolean>("isCastToNotNull")
}
val REDUNDANT_NULLABLE by warning<KtTypeReference>(PositioningStrategy.REDUNDANT_NULLABLE)
}
val REFLECTION by object : DiagnosticGroup("Reflection") {
@@ -111,6 +111,7 @@ enum class PositioningStrategy(private val strategy: String? = null) {
PROPERTY_DELEGATE,
IMPORT_ALIAS,
DECLARATION_START_TO_NAME,
REDUNDANT_NULLABLE,
;
@@ -395,6 +395,7 @@ object FirErrors {
val TYPE_VARIANCE_CONFLICT by error4<PsiElement, FirTypeParameterSymbol, Variance, Variance, ConeKotlinType>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val TYPE_VARIANCE_CONFLICT_IN_EXPANDED_TYPE by error4<PsiElement, FirTypeParameterSymbol, Variance, Variance, ConeKotlinType>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val SMARTCAST_IMPOSSIBLE by error4<KtExpression, ConeKotlinType, FirExpression, String, Boolean>()
val REDUNDANT_NULLABLE by warning0<KtTypeReference>(SourceElementPositioningStrategies.REDUNDANT_NULLABLE)
// Reflection
val EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED by error1<KtExpression, FirCallableSymbol<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
@@ -12,6 +12,6 @@ object CommonTypeCheckers : TypeCheckers() {
FirTypeAnnotationChecker,
FirSuspendModifierChecker,
FirDeprecatedTypeChecker,
FirOptInUsageTypeRefChecker,
FirOptInUsageTypeRefChecker
)
}
@@ -0,0 +1,15 @@
/*
* 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
import org.jetbrains.kotlin.fir.analysis.checkers.extended.RedundantNullableChecker
import org.jetbrains.kotlin.fir.analysis.checkers.type.*
object ExtendedTypeCheckers : TypeCheckers() {
override val typeRefCheckers: Set<FirTypeRefChecker> = setOf(
RedundantNullableChecker
)
}
@@ -6,13 +6,11 @@
package org.jetbrains.kotlin.fir.analysis.checkers
import com.intellij.lang.LighterASTNode
import com.intellij.openapi.util.Ref
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiNameIdentifierOwner
import com.intellij.psi.impl.source.tree.LeafPsiElement
import org.jetbrains.kotlin.KtLightSourceElement
import org.jetbrains.kotlin.KtPsiSourceElement
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.*
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.diagnostics.getAncestors
import org.jetbrains.kotlin.diagnostics.nameIdentifier
@@ -40,6 +38,8 @@ interface SourceNavigator {
fun FirValueParameterSymbol.isCatchElementParameter(): Boolean
fun FirTypeRef.isRedundantNullable(): Boolean
companion object {
private val lightTreeInstance = LightTreeSourceNavigator()
@@ -90,6 +90,19 @@ open class LightTreeSourceNavigator : SourceNavigator {
return source?.getParentOfParent()?.tokenType == KtNodeTypes.CATCH
}
override fun FirTypeRef.isRedundantNullable(): Boolean {
val source = source ?: return false
val ref = Ref<Array<LighterASTNode?>>()
val firstChild = getNullableChild(source, source.lighterASTNode, ref) ?: return false
return getNullableChild(source, firstChild, ref) != null
}
private fun getNullableChild(source: KtSourceElement, node: LighterASTNode, ref: Ref<Array<LighterASTNode?>>): LighterASTNode? {
source.treeStructure.getChildren(node, ref)
val firstChild = ref.get().firstOrNull() ?: return null
return if (firstChild.tokenType != KtNodeTypes.NULLABLE_TYPE) null else firstChild
}
private fun KtSourceElement?.getParentOfParent(): LighterASTNode? {
val source = this ?: return null
var parent = source.treeStructure.getParent(source.lighterASTNode)
@@ -131,4 +144,11 @@ object PsiSourceNavigator : LightTreeSourceNavigator() {
override fun FirValueParameterSymbol.isCatchElementParameter(): Boolean {
return source?.psi<PsiElement>()?.parent?.parent is KtCatchClause
}
override fun FirTypeRef.isRedundantNullable(): Boolean {
val source = source ?: return false
val typeReference = (source.psi as? KtTypeReference) ?: return false
val typeElement = typeReference.typeElement as? KtNullableType ?: return false
return typeElement.innerType is KtNullableType
}
}
@@ -0,0 +1,41 @@
/*
* 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.extended
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.checkers.SourceNavigator
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.toClassLikeSymbol
import org.jetbrains.kotlin.fir.analysis.checkers.type.FirTypeRefChecker
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REDUNDANT_NULLABLE
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol
import org.jetbrains.kotlin.fir.types.*
object RedundantNullableChecker : FirTypeRefChecker() {
override fun check(typeRef: FirTypeRef, context: CheckerContext, reporter: DiagnosticReporter) {
if (typeRef !is FirResolvedTypeRef || typeRef.isMarkedNullable != true) return
var symbol = typeRef.toClassLikeSymbol(context.session)
if (symbol is FirTypeAliasSymbol) {
while (symbol is FirTypeAliasSymbol) {
val resolvedExpandedTypeRef = symbol.resolvedExpandedTypeRef
if (resolvedExpandedTypeRef.type.isMarkedNullable) {
reporter.reportOn(typeRef.source, REDUNDANT_NULLABLE, context)
break
} else {
symbol = resolvedExpandedTypeRef.toClassLikeSymbol(context.session)
}
}
} else {
with(SourceNavigator.forElement(typeRef)) {
if (typeRef.isRedundantNullable()) {
reporter.reportOn(typeRef.source, REDUNDANT_NULLABLE, context)
}
}
}
}
}
@@ -236,14 +236,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INFIX_MODIFIER_RE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INITIALIZATION_BEFORE_DECLARATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INITIALIZER_REQUIRED_FOR_DESTRUCTURING_DECLARATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INITIALIZER_TYPE_MISMATCH
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VALUE_CLASS_CANNOT_BE_RECURSIVE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VALUE_CLASS_CANNOT_EXTEND_CLASSES
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VALUE_CLASS_CANNOT_IMPLEMENT_INTERFACE_BY_DELEGATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VALUE_CLASS_CONSTRUCTOR_NOT_FINAL_READ_ONLY_PARAMETER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INLINE_CLASS_CONSTRUCTOR_WRONG_PARAMETERS_SIZE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VALUE_CLASS_HAS_INAPPLICABLE_PARAMETER_TYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VALUE_CLASS_NOT_FINAL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VALUE_CLASS_NOT_TOP_LEVEL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INLINE_PROPERTY_WITH_BACKING_FIELD
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INLINE_SUSPEND_FUNCTION_TYPE_UNSUPPORTED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INNER_CLASS_INSIDE_VALUE_CLASS
@@ -394,6 +387,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REDUNDANT_LABEL_W
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REDUNDANT_MODALITY_MODIFIER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REDUNDANT_MODIFIER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REDUNDANT_MODIFIER_FOR_TARGET
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REDUNDANT_NULLABLE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REDUNDANT_OPEN_IN_INTERFACE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REDUNDANT_PROJECTION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REDUNDANT_RETURN_UNIT_TYPE
@@ -501,6 +495,13 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USELESS_ELVIS_RIG
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USELESS_IS_CHECK
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USELESS_VARARG_ON_PARAMETER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VALUE_CLASS_CANNOT_BE_CLONEABLE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VALUE_CLASS_CANNOT_BE_RECURSIVE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VALUE_CLASS_CANNOT_EXTEND_CLASSES
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VALUE_CLASS_CANNOT_IMPLEMENT_INTERFACE_BY_DELEGATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VALUE_CLASS_CONSTRUCTOR_NOT_FINAL_READ_ONLY_PARAMETER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VALUE_CLASS_HAS_INAPPLICABLE_PARAMETER_TYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VALUE_CLASS_NOT_FINAL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VALUE_CLASS_NOT_TOP_LEVEL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VALUE_PARAMETER_WITH_NO_TYPE_ANNOTATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VAL_OR_VAR_ON_CATCH_PARAMETER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VAL_OR_VAR_ON_FUN_PARAMETER
@@ -622,6 +623,7 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
map.put(SUPERTYPE_IS_EXTENSION_FUNCTION_TYPE, "Extension function type is not allowed as supertypes")
map.put(SINGLETON_IN_SUPERTYPE, "Cannot inherit from a singleton")
map.put(NULLABLE_SUPERTYPE, "A supertype cannot be nullable")
map.put(REDUNDANT_NULLABLE, "Redundant '?'")
map.put(MANY_CLASSES_IN_SUPERTYPE_LIST, "Only one class may appear in a supertype list")
map.put(SUPERTYPE_APPEARS_TWICE, "A supertype appears twice")
map.put(CLASS_IN_SUPERTYPE_FOR_ENUM, "Enum class cannot inherit from classes")