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
}
abstract fun addSuppressedDiagnostics(
diagnosticNames: Collection<String>,
allInfosSuppressed: Boolean,
allWarningsSuppressed: Boolean,
allErrorsSuppressed: Boolean
): PersistentCheckerContext
}
class PersistentCheckerContext private constructor(
@@ -111,7 +118,7 @@ class PersistentCheckerContext private constructor(
)
}
fun addSuppressedDiagnostics(
override fun addSuppressedDiagnostics(
diagnosticNames: Collection<String>,
allInfosSuppressed: 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.reportOn
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.lexer.KtTokens
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.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
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.FirErrors
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.resolve.calls.isPotentiallyArray
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 javaCloneableType = ClassId.fromString("java/lang/Cloneable").constructClassLikeType(emptyArray(), false)
@Suppress("NAME_SHADOWING")
override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (!declaration.isInlineOrValueClass()) {
return
@@ -41,7 +44,7 @@ object FirInlineClassDeclarationChecker : FirRegularClassChecker() {
for (supertypeEntry in declaration.superTypeRefs) {
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 -> {
val bodySource = innerDeclaration.body!!.source
reporter.reportOn(bodySource, FirErrors.SECONDARY_CONSTRUCTOR_WITH_BODY_INSIDE_INLINE_CLASS, context)
val body = innerDeclaration.body!!
withSuppressedDiagnostics(innerDeclaration, context) { context ->
reporter.reportOnWithSuppression(
body, FirErrors.SECONDARY_CONSTRUCTOR_WITH_BODY_INSIDE_INLINE_CLASS, context
)
}
}
}
}
is FirRegularClass -> {
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 -> {
val functionName = innerDeclaration.name.asString()
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 -> {
if (innerDeclaration.isSynthetic) {
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 -> {
@@ -92,16 +107,18 @@ object FirInlineClassDeclarationChecker : FirRegularClassChecker() {
} else {
when {
innerDeclaration.delegate != null ->
reporter.reportOn(
innerDeclaration.delegate!!.source,
FirErrors.DELEGATED_PROPERTY_INSIDE_INLINE_CLASS,
context
)
withSuppressedDiagnostics(innerDeclaration, context) { context ->
reporter.reportOn(
innerDeclaration.delegate!!.source,
FirErrors.DELEGATED_PROPERTY_INSIDE_INLINE_CLASS,
context
)
}
innerDeclaration.hasBackingField &&
innerDeclaration.source?.kind !is FirFakeSourceElementKind ->
reporter.reportOn(
innerDeclaration.source,
reporter.reportOnWithSuppression(
innerDeclaration,
FirErrors.PROPERTY_WITH_BACKING_FIELD_INSIDE_INLINE_CLASS,
context
)
@@ -117,32 +134,36 @@ object FirInlineClassDeclarationChecker : FirRegularClassChecker() {
}
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
}
when {
primaryConstructorParameter.isNotFinalReadOnly(primaryConstructorProperty) ->
reporter.reportOn(
primaryConstructorParameter.source,
FirErrors.INLINE_CLASS_CONSTRUCTOR_NOT_FINAL_READ_ONLY_PARAMETER,
context
)
withSuppressedDiagnostics(primaryConstructor, context) { context ->
withSuppressedDiagnostics(primaryConstructorParameter, context) { context ->
when {
primaryConstructorParameter.isNotFinalReadOnly(primaryConstructorProperty) ->
reporter.reportOn(
primaryConstructorParameter.source,
FirErrors.INLINE_CLASS_CONSTRUCTOR_NOT_FINAL_READ_ONLY_PARAMETER,
context
)
primaryConstructorParameter.returnTypeRef.isInapplicableParameterType() ->
reporter.reportOn(
primaryConstructorParameter.returnTypeRef.source,
FirErrors.INLINE_CLASS_HAS_INAPPLICABLE_PARAMETER_TYPE,
primaryConstructorParameter.returnTypeRef.coneType,
context
)
primaryConstructorParameter.returnTypeRef.isInapplicableParameterType() ->
reporter.reportOn(
primaryConstructorParameter.returnTypeRef.source,
FirErrors.INLINE_CLASS_HAS_INAPPLICABLE_PARAMETER_TYPE,
primaryConstructorParameter.returnTypeRef.coneType,
context
)
primaryConstructorParameter.returnTypeRef.coneType.isRecursiveInlineClassType(context.session) ->
reporter.reportOn(
primaryConstructorParameter.returnTypeRef.source,
FirErrors.INLINE_CLASS_CANNOT_BE_RECURSIVE,
context
)
primaryConstructorParameter.returnTypeRef.coneType.isRecursiveInlineClassType(context.session) ->
reporter.reportOnWithSuppression(
primaryConstructorParameter.returnTypeRef,
FirErrors.INLINE_CLASS_CANNOT_BE_RECURSIVE,
context
)
}
}
}
}
@@ -33,12 +33,6 @@ abstract class AbstractDiagnosticCollector(
override val scopeSession: ScopeSession = ScopeSession(),
returnTypeCalculator: ReturnTypeCalculator = ReturnTypeCalculatorForFullBodyResolve()
) : 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<*>> {
if (!componentsInitialized) {
throw IllegalStateException("Components are not initialized")
@@ -307,16 +301,7 @@ abstract class AbstractDiagnosticCollector(
}
private fun addSuppressedDiagnosticsToContext(annotationContainer: FirAnnotationContainer) {
val annotations = annotationContainer.annotations.filter {
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? }
}
}
val arguments = getDiagnosticsSuppressedForContainer(annotationContainer) ?: return
context = context.addSuppressedDiagnostics(
arguments,
allInfosSuppressed = SUPPRESS_ALL_INFOS in arguments,
@@ -334,6 +319,25 @@ abstract class AbstractDiagnosticCollector(
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) {
@@ -6,8 +6,11 @@
package org.jetbrains.kotlin.fir.analysis.diagnostics
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.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector
abstract class DiagnosticReporter {
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) }
}
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)
}
}