[FIR] Add CONCURRENT_HASH_MAP_CONTAINS_OPERATOR
This commit is contained in:
committed by
teamcityserver
parent
0ffd6ccb34
commit
5760a94ab4
+1
@@ -173,5 +173,6 @@ object JVM_DIAGNOSTICS_LIST : DiagnosticList("FirJvmErrors") {
|
||||
PositioningStrategy.REFERENCE_BY_QUALIFIED
|
||||
)
|
||||
val SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC by error<PsiElement>(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED)
|
||||
val CONCURRENT_HASH_MAP_CONTAINS_OPERATOR by deprecationError<PsiElement>(ProhibitConcurrentHashMapContains)
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.analysis.diagnostics.jvm
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.config.LanguageFeature.DefaultMethodsCallFromJava6TargetError
|
||||
import org.jetbrains.kotlin.config.LanguageFeature.ProhibitConcurrentHashMapContains
|
||||
import org.jetbrains.kotlin.config.LanguageFeature.ProhibitJvmOverloadsOnConstructorsOfAnnotationClasses
|
||||
import org.jetbrains.kotlin.config.LanguageFeature.RepeatableAnnotationContainerConstraints
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.*
|
||||
@@ -116,5 +117,6 @@ object FirJvmErrors {
|
||||
val DEFAULT_METHOD_CALL_FROM_JAVA6_TARGET by deprecationError0<PsiElement>(DefaultMethodsCallFromJava6TargetError, SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
||||
val INTERFACE_STATIC_METHOD_CALL_FROM_JAVA6_TARGET by deprecationError0<PsiElement>(DefaultMethodsCallFromJava6TargetError, SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
||||
val SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC by error0<PsiElement>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
|
||||
val CONCURRENT_HASH_MAP_CONTAINS_OPERATOR by deprecationError0<PsiElement>(ProhibitConcurrentHashMapContains)
|
||||
|
||||
}
|
||||
|
||||
+2
-1
@@ -23,7 +23,8 @@ object JvmExpressionCheckers : ExpressionCheckers() {
|
||||
get() = setOf(
|
||||
FirJavaGenericVarianceViolationTypeChecker,
|
||||
FirSuperCallWithDefaultsChecker,
|
||||
FirJvmSuspensionPointInsideMutexLockChecker
|
||||
FirJvmSuspensionPointInsideMutexLockChecker,
|
||||
FirJvmInconsistentOperatorFromJavaCallChecker
|
||||
)
|
||||
|
||||
override val annotationCheckers: Set<FirAnnotationChecker>
|
||||
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.FirSourceElement
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirFunctionCallChecker
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.overriddenFunctions
|
||||
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.containingClass
|
||||
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
|
||||
import org.jetbrains.kotlin.fir.expressions.FirFunctionCallOrigin
|
||||
import org.jetbrains.kotlin.fir.expressions.toResolvedCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.types.coneType
|
||||
import org.jetbrains.kotlin.fir.types.isAny
|
||||
import org.jetbrains.kotlin.fir.types.isNullableAny
|
||||
import org.jetbrains.kotlin.fir.types.lowerBoundIfFlexible
|
||||
import org.jetbrains.kotlin.name.CallableId
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
object FirJvmInconsistentOperatorFromJavaCallChecker : FirFunctionCallChecker() {
|
||||
private val CONCURRENT_HASH_MAP_CALLABLE_ID = CallableId(
|
||||
ClassId.fromString("java/util/concurrent/ConcurrentHashMap"),
|
||||
Name.identifier("contains")
|
||||
)
|
||||
|
||||
override fun check(expression: FirFunctionCall, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
val callableSymbol = expression.calleeReference.toResolvedCallableSymbol() as? FirNamedFunctionSymbol ?: return
|
||||
if (callableSymbol.name != OperatorNameConventions.CONTAINS) return
|
||||
val valueParameterSymbol = callableSymbol.valueParameterSymbols.singleOrNull() ?: return
|
||||
val type = valueParameterSymbol.resolvedReturnTypeRef.coneType.lowerBoundIfFlexible()
|
||||
if (!type.isAny && !type.isNullableAny) return
|
||||
|
||||
if (expression.origin != FirFunctionCallOrigin.Operator || expression.origin.ordinal != 2) return
|
||||
|
||||
callableSymbol.check(expression.calleeReference.source, context, reporter)
|
||||
}
|
||||
|
||||
fun FirNamedFunctionSymbol.check(source: FirSourceElement?, context: CheckerContext, reporter: DiagnosticReporter): Boolean {
|
||||
if (callableId == CONCURRENT_HASH_MAP_CALLABLE_ID) {
|
||||
reporter.reportOn(source, FirJvmErrors.CONCURRENT_HASH_MAP_CONTAINS_OPERATOR, context)
|
||||
return true
|
||||
}
|
||||
|
||||
val containingClass = containingClass()?.toFirRegularClassSymbol(context.session) ?: return false
|
||||
val overriddenFunctions = overriddenFunctions(containingClass, context)
|
||||
for (overriddenFunction in overriddenFunctions) {
|
||||
if (overriddenFunction is FirNamedFunctionSymbol && overriddenFunction.check(source, context, reporter)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
+8
-2
@@ -5,12 +5,12 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.jvm.diagnostics
|
||||
|
||||
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.CONCURRENT_HASH_MAP_CONTAINS_OPERATOR
|
||||
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.DELEGATION_BY_IN_JVM_RECORD
|
||||
@@ -23,9 +23,9 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.EXTERNAL_D
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.FIELD_IN_JVM_RECORD
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.ILLEGAL_JAVA_LANG_RECORD_SUPERTYPE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.ILLEGAL_JVM_NAME
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.INAPPLICABLE_JVM_NAME
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.INAPPLICABLE_JVM_FIELD
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.INAPPLICABLE_JVM_FIELD_WARNING
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.INAPPLICABLE_JVM_NAME
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.INNER_JVM_RECORD
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.INTERFACE_CANT_CALL_DEFAULT_METHOD_VIA_SUPER
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.INTERFACE_STATIC_METHOD_CALL_FROM_JAVA6_TARGET
|
||||
@@ -264,6 +264,12 @@ object FirJvmDefaultErrorMessages {
|
||||
"The ''{0}'' suspension point is inside a critical section",
|
||||
SYMBOL
|
||||
)
|
||||
map.put(
|
||||
CONCURRENT_HASH_MAP_CONTAINS_OPERATOR,
|
||||
"Method 'contains' from ConcurrentHashMap may have unexpected semantics: it calls 'containsValue' instead of 'containsKey'. " +
|
||||
"Use explicit form of the call to 'containsKey'/'containsValue'/'contains' or cast the value to kotlin.collections.Map instead. " +
|
||||
"See https://youtrack.jetbrains.com/issue/KT-18053 for more details"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -185,6 +185,13 @@ fun CheckerContext.findClosestClassOrObject(): FirClass? {
|
||||
fun FirSimpleFunction.overriddenFunctions(
|
||||
containingClass: FirClassSymbol<*>,
|
||||
context: CheckerContext
|
||||
): List<FirFunctionSymbol<*>> {
|
||||
return symbol.overriddenFunctions(containingClass, context)
|
||||
}
|
||||
|
||||
fun FirNamedFunctionSymbol.overriddenFunctions(
|
||||
containingClass: FirClassSymbol<*>,
|
||||
context: CheckerContext
|
||||
): List<FirFunctionSymbol<*>> {
|
||||
val firTypeScope = containingClass.unsubstitutedScope(
|
||||
context.sessionHolder.session,
|
||||
@@ -193,8 +200,8 @@ fun FirSimpleFunction.overriddenFunctions(
|
||||
)
|
||||
|
||||
val overriddenFunctions = mutableListOf<FirFunctionSymbol<*>>()
|
||||
firTypeScope.processFunctionsByName(symbol.callableId.callableName) { }
|
||||
firTypeScope.processOverriddenFunctions(symbol) {
|
||||
firTypeScope.processFunctionsByName(callableId.callableName) { }
|
||||
firTypeScope.processOverriddenFunctions(this) {
|
||||
overriddenFunctions.add(it)
|
||||
ProcessorAction.NEXT
|
||||
}
|
||||
|
||||
+16
-16
@@ -18,10 +18,10 @@ class C : java.util.concurrent.ConcurrentHashMap<String, Int>() {
|
||||
|
||||
fun main() {
|
||||
val hm = java.util.concurrent.ConcurrentHashMap<String, Int>()
|
||||
"" in hm
|
||||
"" !in hm
|
||||
1 !in hm
|
||||
2 in hm
|
||||
"" <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_WARNING!>in<!> hm
|
||||
"" <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_WARNING!>!in<!> hm
|
||||
1 <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_WARNING!>!in<!> hm
|
||||
2 <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_WARNING!>in<!> hm
|
||||
|
||||
hm.contains("")
|
||||
hm.contains(1)
|
||||
@@ -32,10 +32,10 @@ fun main() {
|
||||
1 !in (hm as Map<String, Int>)
|
||||
|
||||
val a = A()
|
||||
"" in a
|
||||
"" !in a
|
||||
1 !in a
|
||||
2 in a
|
||||
"" <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_WARNING!>in<!> a
|
||||
"" <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_WARNING!>!in<!> a
|
||||
1 <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_WARNING!>!in<!> a
|
||||
2 <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_WARNING!>in<!> a
|
||||
|
||||
' ' in a
|
||||
' ' !in a
|
||||
@@ -48,10 +48,10 @@ fun main() {
|
||||
1 !in (a as Map<String, Int>)
|
||||
|
||||
val b = B()
|
||||
"" in b
|
||||
"" !in b
|
||||
1 !in b
|
||||
2 in b
|
||||
"" <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_WARNING!>in<!> b
|
||||
"" <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_WARNING!>!in<!> b
|
||||
1 <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_WARNING!>!in<!> b
|
||||
2 <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_WARNING!>in<!> b
|
||||
|
||||
b.contains("")
|
||||
b.contains(1)
|
||||
@@ -63,10 +63,10 @@ fun main() {
|
||||
|
||||
// Actually, we could've allow calls here because the owner explicitly declared as operator, but semantics is still weird
|
||||
val c = C()
|
||||
"" in c
|
||||
"" !in c
|
||||
1 !in c
|
||||
2 in c
|
||||
"" <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_WARNING!>in<!> c
|
||||
"" <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_WARNING!>!in<!> c
|
||||
1 <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_WARNING!>!in<!> c
|
||||
2 <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_WARNING!>in<!> c
|
||||
|
||||
c.contains("")
|
||||
c.contains(1)
|
||||
|
||||
+16
-17
@@ -18,10 +18,10 @@ class C : java.util.concurrent.ConcurrentHashMap<String, Int>() {
|
||||
|
||||
fun main() {
|
||||
val hm = java.util.concurrent.ConcurrentHashMap<String, Int>()
|
||||
"" in hm
|
||||
"" !in hm
|
||||
1 !in hm
|
||||
2 in hm
|
||||
"" <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_ERROR!>in<!> hm
|
||||
"" <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_ERROR!>!in<!> hm
|
||||
1 <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_ERROR!>!in<!> hm
|
||||
2 <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_ERROR!>in<!> hm
|
||||
|
||||
hm.contains("")
|
||||
hm.contains(1)
|
||||
@@ -32,10 +32,10 @@ fun main() {
|
||||
1 !in (hm as Map<String, Int>)
|
||||
|
||||
val a = A()
|
||||
"" in a
|
||||
"" !in a
|
||||
1 !in a
|
||||
2 in a
|
||||
"" <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_ERROR!>in<!> a
|
||||
"" <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_ERROR!>!in<!> a
|
||||
1 <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_ERROR!>!in<!> a
|
||||
2 <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_ERROR!>in<!> a
|
||||
|
||||
' ' in a
|
||||
' ' !in a
|
||||
@@ -48,10 +48,10 @@ fun main() {
|
||||
1 !in (a as Map<String, Int>)
|
||||
|
||||
val b = B()
|
||||
"" in b
|
||||
"" !in b
|
||||
1 !in b
|
||||
2 in b
|
||||
"" <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_ERROR!>in<!> b
|
||||
"" <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_ERROR!>!in<!> b
|
||||
1 <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_ERROR!>!in<!> b
|
||||
2 <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_ERROR!>in<!> b
|
||||
|
||||
b.contains("")
|
||||
b.contains(1)
|
||||
@@ -63,10 +63,10 @@ fun main() {
|
||||
|
||||
// Actually, we could've allow calls here because the owner explicitly declared as operator, but semantics is still weird
|
||||
val c = C()
|
||||
"" in c
|
||||
"" !in c
|
||||
1 !in c
|
||||
2 in c
|
||||
"" <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_ERROR!>in<!> c
|
||||
"" <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_ERROR!>!in<!> c
|
||||
1 <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_ERROR!>!in<!> c
|
||||
2 <!CONCURRENT_HASH_MAP_CONTAINS_OPERATOR_ERROR!>in<!> c
|
||||
|
||||
c.contains("")
|
||||
c.contains(1)
|
||||
@@ -76,4 +76,3 @@ fun main() {
|
||||
1 in (c as Map<String, Int>)
|
||||
1 !in (c as Map<String, Int>)
|
||||
}
|
||||
|
||||
|
||||
+12
@@ -4047,4 +4047,16 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirJvmErrors.CONCURRENT_HASH_MAP_CONTAINS_OPERATOR.errorFactory) { firDiagnostic ->
|
||||
ConcurrentHashMapContainsOperatorErrorImpl(
|
||||
firDiagnostic as FirPsiDiagnostic,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirJvmErrors.CONCURRENT_HASH_MAP_CONTAINS_OPERATOR.warningFactory) { firDiagnostic ->
|
||||
ConcurrentHashMapContainsOperatorWarningImpl(
|
||||
firDiagnostic as FirPsiDiagnostic,
|
||||
token,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+8
@@ -2816,4 +2816,12 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
|
||||
override val diagnosticClass get() = SubclassCantCallCompanionProtectedNonStatic::class
|
||||
}
|
||||
|
||||
abstract class ConcurrentHashMapContainsOperatorError : KtFirDiagnostic<PsiElement>() {
|
||||
override val diagnosticClass get() = ConcurrentHashMapContainsOperatorError::class
|
||||
}
|
||||
|
||||
abstract class ConcurrentHashMapContainsOperatorWarning : KtFirDiagnostic<PsiElement>() {
|
||||
override val diagnosticClass get() = ConcurrentHashMapContainsOperatorWarning::class
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+14
@@ -4581,3 +4581,17 @@ internal class SubclassCantCallCompanionProtectedNonStaticImpl(
|
||||
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class ConcurrentHashMapContainsOperatorErrorImpl(
|
||||
firDiagnostic: FirPsiDiagnostic,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.ConcurrentHashMapContainsOperatorError(), KtAbstractFirDiagnostic<PsiElement> {
|
||||
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class ConcurrentHashMapContainsOperatorWarningImpl(
|
||||
firDiagnostic: FirPsiDiagnostic,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.ConcurrentHashMapContainsOperatorWarning(), KtAbstractFirDiagnostic<PsiElement> {
|
||||
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user