[FIR] Report warning when error is suppressed

#KT-61129
This commit is contained in:
Kirill Rakhman
2023-08-14 10:59:35 +02:00
committed by Space Team
parent 769e4ac0ac
commit c32a0a83f9
10 changed files with 68 additions and 6 deletions
@@ -271,6 +271,13 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirErrors.ERROR_SUPPRESSION) { firDiagnostic ->
ErrorSuppressionImpl(
firDiagnostic.a,
firDiagnostic as KtPsiDiagnostic,
token,
)
}
add(FirErrors.INVISIBLE_REFERENCE) { firDiagnostic ->
InvisibleReferenceImpl(
firSymbolBuilder.buildSymbol(firDiagnostic.a),
@@ -232,6 +232,11 @@ sealed interface KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = InnerOnTopLevelScriptClassWarning::class
}
interface ErrorSuppression : KtFirDiagnostic<PsiElement> {
override val diagnosticClass get() = ErrorSuppression::class
val diagnosticName: String
}
interface InvisibleReference : KtFirDiagnostic<PsiElement> {
override val diagnosticClass get() = InvisibleReference::class
val reference: KtSymbol
@@ -264,6 +264,12 @@ internal class InnerOnTopLevelScriptClassWarningImpl(
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<PsiElement>(firDiagnostic, token), KtFirDiagnostic.InnerOnTopLevelScriptClassWarning
internal class ErrorSuppressionImpl(
override val diagnosticName: String,
firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<PsiElement>(firDiagnostic, token), KtFirDiagnostic.ErrorSuppression
internal class InvisibleReferenceImpl(
override val reference: KtSymbol,
override val visible: Visibility,
@@ -105,6 +105,9 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
parameter<CallableId>("callableId")
}
val INNER_ON_TOP_LEVEL_SCRIPT_CLASS by deprecationError<PsiElement>(LanguageFeature.ProhibitScriptTopLevelInnerClasses)
val ERROR_SUPPRESSION by warning<PsiElement> {
parameter<String>("diagnosticName")
}
}
val UNRESOLVED by object : DiagnosticGroup("Unresolved") {
@@ -152,6 +152,7 @@ object FirErrors {
val VAL_OR_VAR_ON_SECONDARY_CONSTRUCTOR_PARAMETER by error1<KtParameter, KtKeywordToken>(SourceElementPositioningStrategies.VAL_OR_VAR_NODE)
val INVISIBLE_SETTER by error3<PsiElement, FirPropertySymbol, Visibility, CallableId>(SourceElementPositioningStrategies.SELECTOR_BY_QUALIFIED)
val INNER_ON_TOP_LEVEL_SCRIPT_CLASS by deprecationError0<PsiElement>(ProhibitScriptTopLevelInnerClasses)
val ERROR_SUPPRESSION by warning1<PsiElement, String>()
// Unresolved
val INVISIBLE_REFERENCE by error3<PsiElement, FirBasedSymbol<*>, Visibility, ClassId?>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.fir.FirAnnotationContainer
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirInlineDeclarationChecker
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.expressions.FirGetClassCall
@@ -58,6 +59,12 @@ abstract class CheckerContext : DiagnosticContext {
override fun isDiagnosticSuppressed(diagnostic: KtDiagnostic): Boolean {
val factory = diagnostic.factory
val name = factory.name
if (name == FirErrors.ERROR_SUPPRESSION.name) {
// Can't suppress warning about suppressed error
return false
}
val suppressedByAll = when (factory.severity) {
Severity.INFO -> allInfosSuppressed
Severity.WARNING -> allWarningsSuppressed
@@ -14,17 +14,22 @@ import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.ConstantArgumentKind
import org.jetbrains.kotlin.fir.analysis.checkers.checkConstantArguments
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector
import org.jetbrains.kotlin.fir.analysis.diagnostics.FIR_NON_SUPPRESSIBLE_ERROR_NAMES
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.declarations.findArgumentByName
import org.jetbrains.kotlin.fir.declarations.toAnnotationClassId
import org.jetbrains.kotlin.fir.declarations.unwrapVarargValue
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.languageVersionSettings
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
import org.jetbrains.kotlin.fir.resolve.fqName
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.FirErrorTypeRef
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.resolve.RequireKotlinConstants
object FirAnnotationExpressionChecker : FirAnnotationCallChecker() {
@@ -39,7 +44,8 @@ object FirAnnotationExpressionChecker : FirAnnotationCallChecker() {
override fun check(expression: FirAnnotationCall, context: CheckerContext, reporter: DiagnosticReporter) {
val argumentMapping = expression.argumentMapping.mapping
val fqName = expression.fqName(context.session)
val annotationClassId = expression.toAnnotationClassId(context.session)
val fqName = annotationClassId?.asSingleFqName()
for (arg in argumentMapping.values) {
val argExpression = (arg as? FirNamedArgumentExpression)?.expression ?: (arg as? FirErrorExpression)?.expression ?: arg
checkAnnotationArgumentWithSubElements(argExpression, context.session, reporter, context)
@@ -50,6 +56,7 @@ object FirAnnotationExpressionChecker : FirAnnotationCallChecker() {
checkDeprecatedSinceKotlin(expression.source, fqName, argumentMapping, context, reporter)
checkAnnotationUsedAsAnnotationArgument(expression, context, reporter)
checkNotAClass(expression, context, reporter)
checkErrorSuppression(annotationClassId, argumentMapping, reporter, context)
}
private fun checkAnnotationArgumentWithSubElements(
@@ -220,4 +227,23 @@ object FirAnnotationExpressionChecker : FirAnnotationCallChecker() {
reporter.reportOn(annotationTypeRef.source, FirErrors.NOT_A_CLASS, context)
}
}
private fun checkErrorSuppression(
annotationClassId: ClassId?,
argumentMapping: Map<Name, FirExpression>,
reporter: DiagnosticReporter,
context: CheckerContext,
) {
if (annotationClassId != StandardClassIds.Annotations.Suppress) return
val nameExpressions = argumentMapping[StandardClassIds.Annotations.ParameterNames.suppressNames]?.unwrapVarargValue() ?: return
for (nameExpression in nameExpressions) {
val name = (nameExpression as? FirConstExpression<*>)?.value as? String ?: continue
val parameter = when (name) {
in FIR_NON_SUPPRESSIBLE_ERROR_NAMES -> name
AbstractDiagnosticCollector.SUPPRESS_ALL_ERRORS -> "all errors"
else -> continue
}
reporter.reportOn(nameExpression.source, FirErrors.ERROR_SUPPRESSION, parameter, context)
}
}
}
@@ -17,7 +17,6 @@ import org.jetbrains.kotlin.fir.resolve.SessionHolder
import org.jetbrains.kotlin.fir.symbols.lazyDeclarationResolver
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.StandardClassIds
abstract class AbstractDiagnosticCollector(
@@ -45,8 +44,6 @@ abstract class AbstractDiagnosticCollector(
const val SUPPRESS_ALL_WARNINGS = "warnings"
const val SUPPRESS_ALL_ERRORS = "errors"
private val SUPPRESS_NAMES_NAME = Name.identifier("names")
private fun correctDiagnosticCase(diagnostic: String): String = when (diagnostic) {
SUPPRESS_ALL_INFOS, SUPPRESS_ALL_WARNINGS, SUPPRESS_ALL_ERRORS -> diagnostic
else -> diagnostic.uppercase()
@@ -58,7 +55,9 @@ abstract class AbstractDiagnosticCollector(
for (annotation in annotationContainer.annotations) {
val type = annotation.annotationTypeRef.coneType as? ConeClassLikeType ?: continue
if (type.lookupTag.classId != StandardClassIds.Annotations.Suppress) continue
val argumentValues = annotation.findArgumentByName(SUPPRESS_NAMES_NAME)?.unwrapVarargValue() ?: continue
val argumentValues =
annotation.findArgumentByName(StandardClassIds.Annotations.ParameterNames.suppressNames)?.unwrapVarargValue()
?: continue
for (argumentValue in argumentValues) {
val value = (argumentValue as? FirConstExpression<*>)?.value as? String ?: continue
@@ -205,6 +205,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EQUALITY_NOT_APPL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EQUALITY_NOT_APPLICABLE_WARNING
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ERROR_FROM_JAVA_RESOLUTION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ERROR_IN_CONTRACT_DESCRIPTION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ERROR_SUPPRESSION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPANDED_TYPE_CANNOT_BE_INHERITED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPECTED_CLASS_CONSTRUCTOR_DELEGATION_CALL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPECTED_CLASS_CONSTRUCTOR_PROPERTY_PARAMETER
@@ -748,6 +749,11 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
NAME_OF_CONTAINING_DECLARATION_OR_FILE
)
map.put(INNER_ON_TOP_LEVEL_SCRIPT_CLASS, "Top level script class cannot be inner.")
map.put(
ERROR_SUPPRESSION,
"This code uses error suppression for ''{0}''. While it might compile and work, the compiler behavior is UNSPECIFIED and WON''T BE PRESERVED. Please report your use case to the Kotlin issue tracker instead: https://kotl.in/issue",
TO_STRING
)
map.put(UNRESOLVED_REFERENCE, "Unresolved reference: {0}", NULLABLE_STRING)
map.put(UNRESOLVED_IMPORT, "Unresolved reference: {0}", NULLABLE_STRING) // &
map.put(UNRESOLVED_LABEL, "Unresolved label")
@@ -223,6 +223,8 @@ object StandardClassIds {
val deprecatedSinceKotlinErrorSince = Name.identifier("errorSince")
val deprecatedSinceKotlinHiddenSince = Name.identifier("hiddenSince")
val suppressNames = Name.identifier("names")
val parameterNameName = StandardNames.NAME
}
}