[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.PrivateForInline
|
||||||
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.DiagnosticList
|
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.checkers.generator.diagnostics.model.PositioningStrategy
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
import org.jetbrains.kotlin.name.FqName
|
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 MISC by object : DiagnosticGroup("Misc") {
|
||||||
val INAPPLICABLE_JVM_FIELD by error<KtAnnotationEntry> {
|
val INAPPLICABLE_JVM_FIELD by error<KtAnnotationEntry> {
|
||||||
parameter<String>("message")
|
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.config.LanguageFeature.RepeatableAnnotationContainerConstraints
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.*
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.*
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.SourceElementPositioningStrategies
|
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.fir.types.ConeKotlinType
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
import org.jetbrains.kotlin.name.FqName
|
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_CONTAINER_TARGET_SET_NOT_A_SUBSET by deprecationError2<KtAnnotationEntry, ClassId, ClassId>(RepeatableAnnotationContainerConstraints)
|
||||||
val REPEATABLE_ANNOTATION_HAS_NESTED_CLASS_NAMED_CONTAINER by deprecationError0<KtAnnotationEntry>(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
|
// Misc
|
||||||
val INAPPLICABLE_JVM_FIELD by error1<KtAnnotationEntry, String>()
|
val INAPPLICABLE_JVM_FIELD by error1<KtAnnotationEntry, String>()
|
||||||
val INAPPLICABLE_JVM_FIELD_WARNING by warning1<KtAnnotationEntry, String>()
|
val INAPPLICABLE_JVM_FIELD_WARNING by warning1<KtAnnotationEntry, String>()
|
||||||
|
|||||||
+1
@@ -23,6 +23,7 @@ object JvmExpressionCheckers : ExpressionCheckers() {
|
|||||||
get() = setOf(
|
get() = setOf(
|
||||||
FirJavaGenericVarianceViolationTypeChecker,
|
FirJavaGenericVarianceViolationTypeChecker,
|
||||||
FirSuperCallWithDefaultsChecker,
|
FirSuperCallWithDefaultsChecker,
|
||||||
|
FirJvmSuspensionPointInsideMutexLockChecker
|
||||||
)
|
)
|
||||||
|
|
||||||
override val annotationCheckers: Set<FirAnnotationChecker>
|
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.diagnostics.rendering.CommonRenderers.STRING
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDefaultErrorMessages
|
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.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.FirDiagnosticRenderers.TO_STRING
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.CONFLICTING_JVM_DECLARATIONS
|
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
|
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.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.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.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_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_ABSTRACT
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.SYNCHRONIZED_ON_INLINE
|
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_HAS_NESTED_CLASS_NAMED_CONTAINER,
|
||||||
"Repeatable annotation cannot have a nested class named 'Container'. This name is reserved for auto-generated container class"
|
"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
|
// !DIAGNOSTICS: -UNUSED_PARAMETER, -USELESS_IS_CHECK
|
||||||
// SKIP_TXT
|
// SKIP_TXT
|
||||||
import kotlin.concurrent.withLock
|
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
|
// !DIAGNOSTICS: -UNUSED_PARAMETER, -USELESS_IS_CHECK
|
||||||
// !LANGUAGE: +NewInference
|
// !LANGUAGE: +NewInference
|
||||||
// SKIP_TXT
|
// SKIP_TXT
|
||||||
|
|||||||
+7
@@ -3990,6 +3990,13 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
|||||||
token,
|
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 ->
|
add(FirJvmErrors.INAPPLICABLE_JVM_FIELD) { firDiagnostic ->
|
||||||
InapplicableJvmFieldImpl(
|
InapplicableJvmFieldImpl(
|
||||||
firDiagnostic.a,
|
firDiagnostic.a,
|
||||||
|
|||||||
+5
@@ -2777,6 +2777,11 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
|
|||||||
override val diagnosticClass get() = RepeatableAnnotationHasNestedClassNamedContainerWarning::class
|
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>() {
|
abstract class InapplicableJvmField : KtFirDiagnostic<KtAnnotationEntry>() {
|
||||||
override val diagnosticClass get() = InapplicableJvmField::class
|
override val diagnosticClass get() = InapplicableJvmField::class
|
||||||
abstract val message: String
|
abstract val message: String
|
||||||
|
|||||||
+8
@@ -4515,6 +4515,14 @@ internal class RepeatableAnnotationHasNestedClassNamedContainerWarningImpl(
|
|||||||
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
|
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(
|
internal class InapplicableJvmFieldImpl(
|
||||||
override val message: String,
|
override val message: String,
|
||||||
firDiagnostic: FirPsiDiagnostic,
|
firDiagnostic: FirPsiDiagnostic,
|
||||||
|
|||||||
Reference in New Issue
Block a user