diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt index 60fe67d2353..12c597f55f3 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -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), diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt index 2c0ec66ccb0..40bcfc7c6e1 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt @@ -232,6 +232,11 @@ sealed interface KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = InnerOnTopLevelScriptClassWarning::class } + interface ErrorSuppression : KtFirDiagnostic { + override val diagnosticClass get() = ErrorSuppression::class + val diagnosticName: String + } + interface InvisibleReference : KtFirDiagnostic { override val diagnosticClass get() = InvisibleReference::class val reference: KtSymbol diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 2c912afce13..30757a60842 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -264,6 +264,12 @@ internal class InnerOnTopLevelScriptClassWarningImpl( token: KtLifetimeToken, ) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.InnerOnTopLevelScriptClassWarning +internal class ErrorSuppressionImpl( + override val diagnosticName: String, + firDiagnostic: KtPsiDiagnostic, + token: KtLifetimeToken, +) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.ErrorSuppression + internal class InvisibleReferenceImpl( override val reference: KtSymbol, override val visible: Visibility, diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index 7a470135711..d6759c2c849 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -105,6 +105,9 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { parameter("callableId") } val INNER_ON_TOP_LEVEL_SCRIPT_CLASS by deprecationError(LanguageFeature.ProhibitScriptTopLevelInnerClasses) + val ERROR_SUPPRESSION by warning { + parameter("diagnosticName") + } } val UNRESOLVED by object : DiagnosticGroup("Unresolved") { diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index 42dddd84ece..c9500ddcc23 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -152,6 +152,7 @@ object FirErrors { val VAL_OR_VAR_ON_SECONDARY_CONSTRUCTOR_PARAMETER by error1(SourceElementPositioningStrategies.VAL_OR_VAR_NODE) val INVISIBLE_SETTER by error3(SourceElementPositioningStrategies.SELECTOR_BY_QUALIFIED) val INNER_ON_TOP_LEVEL_SCRIPT_CLASS by deprecationError0(ProhibitScriptTopLevelInnerClasses) + val ERROR_SUPPRESSION by warning1() // Unresolved val INVISIBLE_REFERENCE by error3, Visibility, ClassId?>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/context/CheckerContext.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/context/CheckerContext.kt index bbd611e7aa7..fb749509e39 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/context/CheckerContext.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/context/CheckerContext.kt @@ -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 diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirAnnotationExpressionChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirAnnotationExpressionChecker.kt index d6ea83fd980..fb7a1e26017 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirAnnotationExpressionChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirAnnotationExpressionChecker.kt @@ -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, + 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) + } + } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/AbstractDiagnosticCollector.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/AbstractDiagnosticCollector.kt index 2dd20e1163f..6d64631bebb 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/AbstractDiagnosticCollector.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/AbstractDiagnosticCollector.kt @@ -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 diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt index 6c3e741c4d5..b8e6d1a3066 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt @@ -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") diff --git a/core/compiler.common/src/org/jetbrains/kotlin/name/StandardClassIds.kt b/core/compiler.common/src/org/jetbrains/kotlin/name/StandardClassIds.kt index bb492eda396..2e7f56d6ee2 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/name/StandardClassIds.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/name/StandardClassIds.kt @@ -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 } }