FIR: implement helpers for diagnostic suppression on elements children

This commit is contained in:
Mikhail Glukhikh
2021-03-05 16:21:41 +03:00
parent 15c3269e38
commit 7eb9b1188f
6 changed files with 152 additions and 53 deletions
@@ -44,6 +44,13 @@ abstract class CheckerContext {
return null return null
} }
abstract fun addSuppressedDiagnostics(
diagnosticNames: Collection<String>,
allInfosSuppressed: Boolean,
allWarningsSuppressed: Boolean,
allErrorsSuppressed: Boolean
): PersistentCheckerContext
} }
class PersistentCheckerContext private constructor( class PersistentCheckerContext private constructor(
@@ -111,7 +118,7 @@ class PersistentCheckerContext private constructor(
) )
} }
fun addSuppressedDiagnostics( override fun addSuppressedDiagnostics(
diagnosticNames: Collection<String>, diagnosticNames: Collection<String>,
allInfosSuppressed: Boolean, allInfosSuppressed: Boolean,
allWarningsSuppressed: Boolean, allWarningsSuppressed: Boolean,
@@ -14,7 +14,6 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
object FirConstructorAllowedChecker : FirConstructorChecker() { object FirConstructorAllowedChecker : FirConstructorChecker() {
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
import org.jetbrains.kotlin.fir.FirSourceElement import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.modality import org.jetbrains.kotlin.fir.analysis.checkers.modality
@@ -12,6 +12,8 @@ import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClass
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOnWithSuppression
import org.jetbrains.kotlin.fir.analysis.diagnostics.withSuppressedDiagnostics
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.resolve.calls.isPotentiallyArray import org.jetbrains.kotlin.fir.resolve.calls.isPotentiallyArray
import org.jetbrains.kotlin.fir.resolve.defaultType import org.jetbrains.kotlin.fir.resolve.defaultType
@@ -26,6 +28,7 @@ object FirInlineClassDeclarationChecker : FirRegularClassChecker() {
private val kotlinCloneableType = ClassId.fromString("kotlin/Cloneable").constructClassLikeType(emptyArray(), false) private val kotlinCloneableType = ClassId.fromString("kotlin/Cloneable").constructClassLikeType(emptyArray(), false)
private val javaCloneableType = ClassId.fromString("java/lang/Cloneable").constructClassLikeType(emptyArray(), false) private val javaCloneableType = ClassId.fromString("java/lang/Cloneable").constructClassLikeType(emptyArray(), false)
@Suppress("NAME_SHADOWING")
override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) { override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (!declaration.isInlineOrValueClass()) { if (!declaration.isInlineOrValueClass()) {
return return
@@ -41,7 +44,7 @@ object FirInlineClassDeclarationChecker : FirRegularClassChecker() {
for (supertypeEntry in declaration.superTypeRefs) { for (supertypeEntry in declaration.superTypeRefs) {
if (supertypeEntry.toRegularClass(context.session)?.isInterface != true) { if (supertypeEntry.toRegularClass(context.session)?.isInterface != true) {
reporter.reportOn(supertypeEntry.source, FirErrors.INLINE_CLASS_CANNOT_EXTEND_CLASSES, context) reporter.reportOnWithSuppression(supertypeEntry, FirErrors.INLINE_CLASS_CANNOT_EXTEND_CLASSES, context)
} }
} }
@@ -63,27 +66,39 @@ object FirInlineClassDeclarationChecker : FirRegularClassChecker() {
} }
innerDeclaration.body != null -> { innerDeclaration.body != null -> {
val bodySource = innerDeclaration.body!!.source val body = innerDeclaration.body!!
reporter.reportOn(bodySource, FirErrors.SECONDARY_CONSTRUCTOR_WITH_BODY_INSIDE_INLINE_CLASS, context) withSuppressedDiagnostics(innerDeclaration, context) { context ->
reporter.reportOnWithSuppression(
body, FirErrors.SECONDARY_CONSTRUCTOR_WITH_BODY_INSIDE_INLINE_CLASS, context
)
}
} }
} }
} }
is FirRegularClass -> { is FirRegularClass -> {
if (innerDeclaration.isInner) { if (innerDeclaration.isInner) {
reporter.reportOn(innerDeclaration.source, FirErrors.INNER_CLASS_INSIDE_INLINE_CLASS, context) reporter.reportOnWithSuppression(innerDeclaration, FirErrors.INNER_CLASS_INSIDE_INLINE_CLASS, context)
} }
} }
is FirSimpleFunction -> { is FirSimpleFunction -> {
val functionName = innerDeclaration.name.asString() val functionName = innerDeclaration.name.asString()
if (functionName in reservedFunctionNames) { if (functionName in reservedFunctionNames) {
reporter.reportOn(innerDeclaration.source, FirErrors.RESERVED_MEMBER_INSIDE_INLINE_CLASS, functionName, context) reporter.reportOnWithSuppression(
innerDeclaration, FirErrors.RESERVED_MEMBER_INSIDE_INLINE_CLASS, functionName, context
)
} }
} }
is FirField -> { is FirField -> {
if (innerDeclaration.isSynthetic) { if (innerDeclaration.isSynthetic) {
val delegatedTypeRefSource = (innerDeclaration.returnTypeRef as FirResolvedTypeRef).delegatedTypeRef?.source val delegatedTypeRefSource = (innerDeclaration.returnTypeRef as FirResolvedTypeRef).delegatedTypeRef?.source
reporter.reportOn(delegatedTypeRefSource, FirErrors.INLINE_CLASS_CANNOT_IMPLEMENT_INTERFACE_BY_DELEGATION, context) withSuppressedDiagnostics(innerDeclaration, context) { context ->
reporter.reportOn(
delegatedTypeRefSource,
FirErrors.INLINE_CLASS_CANNOT_IMPLEMENT_INTERFACE_BY_DELEGATION,
context
)
}
} }
} }
is FirProperty -> { is FirProperty -> {
@@ -92,16 +107,18 @@ object FirInlineClassDeclarationChecker : FirRegularClassChecker() {
} else { } else {
when { when {
innerDeclaration.delegate != null -> innerDeclaration.delegate != null ->
reporter.reportOn( withSuppressedDiagnostics(innerDeclaration, context) { context ->
innerDeclaration.delegate!!.source, reporter.reportOn(
FirErrors.DELEGATED_PROPERTY_INSIDE_INLINE_CLASS, innerDeclaration.delegate!!.source,
context FirErrors.DELEGATED_PROPERTY_INSIDE_INLINE_CLASS,
) context
)
}
innerDeclaration.hasBackingField && innerDeclaration.hasBackingField &&
innerDeclaration.source?.kind !is FirFakeSourceElementKind -> innerDeclaration.source?.kind !is FirFakeSourceElementKind ->
reporter.reportOn( reporter.reportOnWithSuppression(
innerDeclaration.source, innerDeclaration,
FirErrors.PROPERTY_WITH_BACKING_FIELD_INSIDE_INLINE_CLASS, FirErrors.PROPERTY_WITH_BACKING_FIELD_INSIDE_INLINE_CLASS,
context context
) )
@@ -117,32 +134,36 @@ object FirInlineClassDeclarationChecker : FirRegularClassChecker() {
} }
if (primaryConstructorParameter == null) { if (primaryConstructorParameter == null) {
reporter.reportOn(primaryConstructor.source, FirErrors.INLINE_CLASS_CONSTRUCTOR_WRONG_PARAMETERS_SIZE, context) reporter.reportOnWithSuppression(primaryConstructor, FirErrors.INLINE_CLASS_CONSTRUCTOR_WRONG_PARAMETERS_SIZE, context)
return return
} }
when { withSuppressedDiagnostics(primaryConstructor, context) { context ->
primaryConstructorParameter.isNotFinalReadOnly(primaryConstructorProperty) -> withSuppressedDiagnostics(primaryConstructorParameter, context) { context ->
reporter.reportOn( when {
primaryConstructorParameter.source, primaryConstructorParameter.isNotFinalReadOnly(primaryConstructorProperty) ->
FirErrors.INLINE_CLASS_CONSTRUCTOR_NOT_FINAL_READ_ONLY_PARAMETER, reporter.reportOn(
context primaryConstructorParameter.source,
) FirErrors.INLINE_CLASS_CONSTRUCTOR_NOT_FINAL_READ_ONLY_PARAMETER,
context
)
primaryConstructorParameter.returnTypeRef.isInapplicableParameterType() -> primaryConstructorParameter.returnTypeRef.isInapplicableParameterType() ->
reporter.reportOn( reporter.reportOn(
primaryConstructorParameter.returnTypeRef.source, primaryConstructorParameter.returnTypeRef.source,
FirErrors.INLINE_CLASS_HAS_INAPPLICABLE_PARAMETER_TYPE, FirErrors.INLINE_CLASS_HAS_INAPPLICABLE_PARAMETER_TYPE,
primaryConstructorParameter.returnTypeRef.coneType, primaryConstructorParameter.returnTypeRef.coneType,
context context
) )
primaryConstructorParameter.returnTypeRef.coneType.isRecursiveInlineClassType(context.session) -> primaryConstructorParameter.returnTypeRef.coneType.isRecursiveInlineClassType(context.session) ->
reporter.reportOn( reporter.reportOnWithSuppression(
primaryConstructorParameter.returnTypeRef.source, primaryConstructorParameter.returnTypeRef,
FirErrors.INLINE_CLASS_CANNOT_BE_RECURSIVE, FirErrors.INLINE_CLASS_CANNOT_BE_RECURSIVE,
context context
) )
}
}
} }
} }
@@ -33,12 +33,6 @@ abstract class AbstractDiagnosticCollector(
override val scopeSession: ScopeSession = ScopeSession(), override val scopeSession: ScopeSession = ScopeSession(),
returnTypeCalculator: ReturnTypeCalculator = ReturnTypeCalculatorForFullBodyResolve() returnTypeCalculator: ReturnTypeCalculator = ReturnTypeCalculatorForFullBodyResolve()
) : SessionHolder { ) : SessionHolder {
companion object {
private const val SUPPRESS_ALL_INFOS = "infos"
private const val SUPPRESS_ALL_WARNINGS = "warnings"
private const val SUPPRESS_ALL_ERRORS = "errors"
}
fun collectDiagnostics(firFile: FirFile): List<FirDiagnostic<*>> { fun collectDiagnostics(firFile: FirFile): List<FirDiagnostic<*>> {
if (!componentsInitialized) { if (!componentsInitialized) {
throw IllegalStateException("Components are not initialized") throw IllegalStateException("Components are not initialized")
@@ -307,16 +301,7 @@ abstract class AbstractDiagnosticCollector(
} }
private fun addSuppressedDiagnosticsToContext(annotationContainer: FirAnnotationContainer) { private fun addSuppressedDiagnosticsToContext(annotationContainer: FirAnnotationContainer) {
val annotations = annotationContainer.annotations.filter { val arguments = getDiagnosticsSuppressedForContainer(annotationContainer) ?: return
val type = it.annotationTypeRef.coneType as? ConeClassLikeType ?: return@filter false
type.lookupTag.classId == StandardClassIds.Suppress
}
if (annotations.isEmpty()) return
val arguments = annotations.flatMap { annotationCall ->
annotationCall.arguments.filterIsInstance<FirVarargArgumentsExpression>().flatMap { varargArgument ->
varargArgument.arguments.mapNotNull { (it as? FirConstExpression<*>)?.value as? String? }
}
}
context = context.addSuppressedDiagnostics( context = context.addSuppressedDiagnostics(
arguments, arguments,
allInfosSuppressed = SUPPRESS_ALL_INFOS in arguments, allInfosSuppressed = SUPPRESS_ALL_INFOS in arguments,
@@ -334,6 +319,25 @@ abstract class AbstractDiagnosticCollector(
currentAction = oldAction currentAction = oldAction
} }
} }
companion object {
const val SUPPRESS_ALL_INFOS = "infos"
const val SUPPRESS_ALL_WARNINGS = "warnings"
const val SUPPRESS_ALL_ERRORS = "errors"
fun getDiagnosticsSuppressedForContainer(annotationContainer: FirAnnotationContainer): List<String>? {
val annotations = annotationContainer.annotations.filter {
val type = it.annotationTypeRef.coneType as? ConeClassLikeType ?: return@filter false
type.lookupTag.classId == StandardClassIds.Suppress
}
if (annotations.isEmpty()) return null
return annotations.flatMap { annotationCall ->
annotationCall.arguments.filterIsInstance<FirVarargArgumentsExpression>().flatMap { varargArgument ->
varargArgument.arguments.mapNotNull { (it as? FirConstExpression<*>)?.value as? String? }
}
}
}
}
} }
enum class DiagnosticCollectorDeclarationAction(val checkInCurrentDeclaration: Boolean, val lookupForNestedDeclaration: Boolean) { enum class DiagnosticCollectorDeclarationAction(val checkInCurrentDeclaration: Boolean, val lookupForNestedDeclaration: Boolean) {
@@ -6,8 +6,11 @@
package org.jetbrains.kotlin.fir.analysis.diagnostics package org.jetbrains.kotlin.fir.analysis.diagnostics
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.fir.FirAnnotationContainer
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSourceElement import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector
abstract class DiagnosticReporter { abstract class DiagnosticReporter {
abstract fun report(diagnostic: FirDiagnostic<*>?, context: CheckerContext) abstract fun report(diagnostic: FirDiagnostic<*>?, context: CheckerContext)
@@ -51,3 +54,69 @@ inline fun <reified T : FirSourceElement, P : PsiElement, A : Any, B : Any, C :
source?.let { report(factory.on(it, a, b, c), context) } source?.let { report(factory.on(it, a, b, c), context) }
} }
inline fun withSuppressedDiagnostics(
element: FirElement,
context: CheckerContext,
f: (CheckerContext) -> Unit
) {
val arguments = (element as? FirAnnotationContainer)?.let { AbstractDiagnosticCollector.getDiagnosticsSuppressedForContainer(it) }
if (arguments != null) {
f(
context.addSuppressedDiagnostics(
arguments,
allInfosSuppressed = AbstractDiagnosticCollector.SUPPRESS_ALL_INFOS in arguments,
allWarningsSuppressed = AbstractDiagnosticCollector.SUPPRESS_ALL_WARNINGS in arguments,
allErrorsSuppressed = AbstractDiagnosticCollector.SUPPRESS_ALL_ERRORS in arguments
)
)
return
}
f(context)
}
inline fun <reified T : FirSourceElement, P : PsiElement> DiagnosticReporter.reportOnWithSuppression(
element: FirElement,
factory: FirDiagnosticFactory0<T, P>,
context: CheckerContext
) {
withSuppressedDiagnostics(element, context) {
reportOn(element.source as? T, factory, it)
}
}
inline fun <reified T : FirSourceElement, P : PsiElement, A : Any> DiagnosticReporter.reportOnWithSuppression(
element: FirElement,
factory: FirDiagnosticFactory1<T, P, A>,
a: A,
context: CheckerContext
) {
withSuppressedDiagnostics(element, context) {
reportOn(element.source as? T, factory, a, it)
}
}
inline fun <reified T : FirSourceElement, P : PsiElement, A : Any, B : Any> DiagnosticReporter.reportOnWithSuppression(
element: FirElement,
factory: FirDiagnosticFactory2<T, P, A, B>,
a: A,
b: B,
context: CheckerContext
) {
withSuppressedDiagnostics(element, context) {
reportOn(element.source as? T, factory, a, b, it)
}
}
inline fun <reified T : FirSourceElement, P : PsiElement, A : Any, B : Any, C : Any> DiagnosticReporter.reportOnWithSuppression(
element: FirElement,
factory: FirDiagnosticFactory3<T, P, A, B, C>,
a: A,
b: B,
c: C,
context: CheckerContext
) {
withSuppressedDiagnostics(element, context) {
reportOn(element.source as? T, factory, a, b, c, it)
}
}