[FIR] Add SUSPENSION_POINT_INSIDE_CRITICAL_SECTION

This commit is contained in:
Ivan Kochurkin
2021-09-08 23:46:37 +03:00
committed by teamcityserver
parent e97933a9e5
commit 0ffd6ccb34
12 changed files with 90 additions and 120 deletions
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.config.LanguageFeature.*
import org.jetbrains.kotlin.fir.PrivateForInline
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.DiagnosticList
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.PositioningStrategy
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
@@ -149,6 +150,12 @@ object JVM_DIAGNOSTICS_LIST : DiagnosticList("FirJvmErrors") {
)
}
val SUSPENSION_POINT by object : DiagnosticGroup("Suspension Point") {
val SUSPENSION_POINT_INSIDE_CRITICAL_SECTION by error<PsiElement>(PositioningStrategy.REFERENCE_BY_QUALIFIED) {
parameter<FirCallableSymbol<*>>("function")
}
}
val MISC by object : DiagnosticGroup("Misc") {
val INAPPLICABLE_JVM_FIELD by error<KtAnnotationEntry> {
parameter<String>("message")
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.config.LanguageFeature.ProhibitJvmOverloadsOnConstru
import org.jetbrains.kotlin.config.LanguageFeature.RepeatableAnnotationContainerConstraints
import org.jetbrains.kotlin.fir.analysis.diagnostics.*
import org.jetbrains.kotlin.fir.analysis.diagnostics.SourceElementPositioningStrategies
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
@@ -105,6 +106,9 @@ object FirJvmErrors {
val REPEATABLE_CONTAINER_TARGET_SET_NOT_A_SUBSET by deprecationError2<KtAnnotationEntry, ClassId, ClassId>(RepeatableAnnotationContainerConstraints)
val REPEATABLE_ANNOTATION_HAS_NESTED_CLASS_NAMED_CONTAINER by deprecationError0<KtAnnotationEntry>(RepeatableAnnotationContainerConstraints)
// Suspension Point
val SUSPENSION_POINT_INSIDE_CRITICAL_SECTION by error1<PsiElement, FirCallableSymbol<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
// Misc
val INAPPLICABLE_JVM_FIELD by error1<KtAnnotationEntry, String>()
val INAPPLICABLE_JVM_FIELD_WARNING by warning1<KtAnnotationEntry, String>()
@@ -23,6 +23,7 @@ object JvmExpressionCheckers : ExpressionCheckers() {
get() = setOf(
FirJavaGenericVarianceViolationTypeChecker,
FirSuperCallWithDefaultsChecker,
FirJvmSuspensionPointInsideMutexLockChecker
)
override val annotationCheckers: Set<FirAnnotationChecker>
@@ -0,0 +1,49 @@
/*
* 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.jvm.checkers.expression
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.context.findClosest
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirFunctionCallChecker
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
import org.jetbrains.kotlin.fir.declarations.utils.isSuspend
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
object FirJvmSuspensionPointInsideMutexLockChecker : FirFunctionCallChecker() {
private val synchronizedCallableId = CallableId(FqName("kotlin"), Name.identifier("synchronized"))
private val withLockCallableId = CallableId(FqName("kotlin.concurrent"), Name.identifier("withLock"))
override fun check(expression: FirFunctionCall, context: CheckerContext, reporter: DiagnosticReporter) {
val symbol = expression.calleeReference.toResolvedCallableSymbol() ?: return
if (!symbol.isSuspend) return
val closestAnonymousFunction = context.findClosest<FirAnonymousFunction>() ?: return
for (call in context.qualifiedAccessOrAnnotationCalls.asReversed()) {
if (call is FirFunctionCall) {
val callableSymbol = call.calleeReference.toResolvedCallableSymbol() ?: continue
if (callableSymbol.callableId == synchronizedCallableId) {
val unwrappedFirstArgument = call.arguments.elementAtOrNull(1)?.unwrapArgument() ?: return
val firstArgumentAnonymousFunction =
(unwrappedFirstArgument as? FirAnonymousFunctionExpression)?.anonymousFunction ?: return
if (closestAnonymousFunction == firstArgumentAnonymousFunction) {
reporter.reportOn(expression.source, FirJvmErrors.SUSPENSION_POINT_INSIDE_CRITICAL_SECTION, symbol, context)
}
return
} else if (callableSymbol.callableId == withLockCallableId) {
reporter.reportOn(expression.source, FirJvmErrors.SUSPENSION_POINT_INSIDE_CRITICAL_SECTION, symbol, context)
return
}
}
}
}
}
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.diagnostics.rendering.Renderers.NAME
import org.jetbrains.kotlin.diagnostics.rendering.CommonRenderers.STRING
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDefaultErrorMessages
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.RENDER_TYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.SYMBOL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.TO_STRING
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.CONFLICTING_JVM_DECLARATIONS
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.DEFAULT_METHOD_CALL_FROM_JAVA6_TARGET
@@ -69,6 +70,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.REPEATED_A
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.REPEATED_ANNOTATION_WITH_CONTAINER
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.STRICTFP_ON_CLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.SUPER_CALL_WITH_DEFAULT_PARAMETERS
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.SUSPENSION_POINT_INSIDE_CRITICAL_SECTION
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.SYNCHRONIZED_IN_INTERFACE
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.SYNCHRONIZED_ON_ABSTRACT
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.SYNCHRONIZED_ON_INLINE
@@ -257,6 +259,11 @@ object FirJvmDefaultErrorMessages {
REPEATABLE_ANNOTATION_HAS_NESTED_CLASS_NAMED_CONTAINER,
"Repeatable annotation cannot have a nested class named 'Container'. This name is reserved for auto-generated container class"
)
map.put(
SUSPENSION_POINT_INSIDE_CRITICAL_SECTION,
"The ''{0}'' suspension point is inside a critical section",
SYMBOL
)
}
}
}
@@ -1,59 +0,0 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER, -USELESS_IS_CHECK
// SKIP_TXT
import kotlin.concurrent.withLock
val lock = java.util.concurrent.locks.ReentrantLock()
fun builder(c: suspend () -> Unit) {}
suspend fun getLock() = lock
suspend fun suspensionPoint() {}
fun test() {
builder {
synchronized(lock) {
suspensionPoint()
}
synchronized(lock) label@{
suspensionPoint()
}
synchronized(lock, { suspensionPoint() })
synchronized(getLock()) {
println("")
}
synchronized(suspend { getLock() } ()) {
println("")
}
synchronized(run { getLock() }) {
println("")
}
lock.withLock {
suspensionPoint()
}
}
}
suspend fun run() {
synchronized(lock) {
suspensionPoint()
}
}
suspend fun ifWhenAndOtherNonsence() {
synchronized(lock) {
if (lock == Any()) {
when (1) {
is Int -> {
return@synchronized 1 + returnsInt()
}
else -> {}
}
} else {}
}
}
suspend fun returnsInt(): Int = 0
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_PARAMETER, -USELESS_IS_CHECK
// SKIP_TXT
import kotlin.concurrent.withLock
@@ -1,61 +0,0 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER, -USELESS_IS_CHECK
// !LANGUAGE: +NewInference
// SKIP_TXT
import kotlin.concurrent.withLock
val lock = java.util.concurrent.locks.ReentrantLock()
fun builder(c: suspend () -> Unit) {}
suspend fun getLock() = lock
suspend fun suspensionPoint() {}
fun test() {
builder {
synchronized(lock) {
suspensionPoint()
}
synchronized(lock) label@{
suspensionPoint()
}
synchronized(lock, { suspensionPoint() })
synchronized(getLock()) {
println("")
}
synchronized(suspend { getLock() } ()) {
println("")
}
synchronized(run { getLock() }) {
println("")
}
lock.withLock {
suspensionPoint()
}
}
}
suspend fun run() {
synchronized(lock) {
suspensionPoint()
}
}
suspend fun ifWhenAndOtherNonsence() {
synchronized(lock) {
if (lock == Any()) {
when (1) {
is Int -> {
return@synchronized 1 + returnsInt()
}
else -> {}
}
} else {}
}
}
suspend fun returnsInt(): Int = 0
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_PARAMETER, -USELESS_IS_CHECK
// !LANGUAGE: +NewInference
// SKIP_TXT
@@ -3990,6 +3990,13 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirJvmErrors.SUSPENSION_POINT_INSIDE_CRITICAL_SECTION) { firDiagnostic ->
SuspensionPointInsideCriticalSectionImpl(
firSymbolBuilder.callableBuilder.buildCallableSymbol(firDiagnostic.a.fir),
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirJvmErrors.INAPPLICABLE_JVM_FIELD) { firDiagnostic ->
InapplicableJvmFieldImpl(
firDiagnostic.a,
@@ -2777,6 +2777,11 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = RepeatableAnnotationHasNestedClassNamedContainerWarning::class
}
abstract class SuspensionPointInsideCriticalSection : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = SuspensionPointInsideCriticalSection::class
abstract val function: KtCallableSymbol
}
abstract class InapplicableJvmField : KtFirDiagnostic<KtAnnotationEntry>() {
override val diagnosticClass get() = InapplicableJvmField::class
abstract val message: String
@@ -4515,6 +4515,14 @@ internal class RepeatableAnnotationHasNestedClassNamedContainerWarningImpl(
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class SuspensionPointInsideCriticalSectionImpl(
override val function: KtCallableSymbol,
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.SuspensionPointInsideCriticalSection(), KtAbstractFirDiagnostic<PsiElement> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class InapplicableJvmFieldImpl(
override val message: String,
firDiagnostic: FirPsiDiagnostic,