[FIR] Add SUSPENSION_POINT_INSIDE_CRITICAL_SECTION
This commit is contained in:
committed by
teamcityserver
parent
e97933a9e5
commit
0ffd6ccb34
+7
@@ -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")
|
||||
|
||||
+4
@@ -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>()
|
||||
|
||||
+1
@@ -23,6 +23,7 @@ object JvmExpressionCheckers : ExpressionCheckers() {
|
||||
get() = setOf(
|
||||
FirJavaGenericVarianceViolationTypeChecker,
|
||||
FirSuperCallWithDefaultsChecker,
|
||||
FirJvmSuspensionPointInsideMutexLockChecker
|
||||
)
|
||||
|
||||
override val annotationCheckers: Set<FirAnnotationChecker>
|
||||
|
||||
+49
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
-59
@@ -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
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER, -USELESS_IS_CHECK
|
||||
// SKIP_TXT
|
||||
import kotlin.concurrent.withLock
|
||||
|
||||
Vendored
-61
@@ -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
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER, -USELESS_IS_CHECK
|
||||
// !LANGUAGE: +NewInference
|
||||
// SKIP_TXT
|
||||
|
||||
+7
@@ -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,
|
||||
|
||||
+5
@@ -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
|
||||
|
||||
+8
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user