Prohibit unsafe covariant conversion for collections invariant in Java
This commit is contained in:
+2
@@ -81,6 +81,8 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension {
|
||||
"Please use the more clear ''::class.java'' syntax to avoid confusion",
|
||||
Renderers.RENDER_TYPE, Renderers.RENDER_TYPE
|
||||
);
|
||||
MAP.put(ErrorsJvm.JAVA_TYPE_MISMATCH,
|
||||
"Java type mismatch expected {1} but found {0}. Use explicit cast", Renderers.RENDER_TYPE, Renderers.RENDER_TYPE);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -67,6 +67,7 @@ public interface ErrorsJvm {
|
||||
DiagnosticFactory0<JetElement> NO_REFLECTION_IN_CLASS_PATH = DiagnosticFactory0.create(WARNING);
|
||||
|
||||
DiagnosticFactory2<JetElement, JetType, JetType> JAVA_CLASS_ON_COMPANION = DiagnosticFactory2.create(WARNING);
|
||||
DiagnosticFactory2<JetExpression, JetType, JetType> JAVA_TYPE_MISMATCH = DiagnosticFactory2.create(ERROR);
|
||||
|
||||
enum NullabilityInformationSource {
|
||||
KOTLIN {
|
||||
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.jvm.platform
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
|
||||
import org.jetbrains.kotlin.load.java.lazy.types.RawTypeTag
|
||||
import org.jetbrains.kotlin.psi.JetExpression
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.AdditionalTypeChecker
|
||||
import org.jetbrains.kotlin.resolve.calls.context.CallResolutionContext
|
||||
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.checker.JetTypeChecker
|
||||
import org.jetbrains.kotlin.types.checker.TypeCheckingProcedure
|
||||
|
||||
public object JavaGenericVarianceViolationTypeChecker : AdditionalTypeChecker {
|
||||
// Prohibits covariant type argument conversions `List<String> -> (MutableList<Any>..List<Any>)` when expected type's lower bound is invariant.
|
||||
// It's needed to prevent accident unsafe covariant conversions of mutable collections.
|
||||
//
|
||||
// Example:
|
||||
// class JavaClass { static void fillWithDefaultObjects(List<Object> list); // add Object's to list }
|
||||
//
|
||||
// val x: MutableList<String>
|
||||
// JavaClass.fillWithDefaultObjects(x) // using `x` after this call may lead to CCE
|
||||
override fun checkType(
|
||||
expression: JetExpression,
|
||||
expressionType: JetType,
|
||||
expressionTypeWithSmartCast: JetType,
|
||||
c: ResolutionContext<*>
|
||||
) {
|
||||
val expectedType = c.expectedType
|
||||
if (TypeUtils.noExpectedType(expectedType) || ErrorUtils.containsErrorType(expectedType) || ErrorUtils.containsUninferredParameter(expectedType)) return
|
||||
|
||||
// optimization: if no arguments or flexibility, everything is OK
|
||||
if (expectedType.arguments.isEmpty() || !expectedType.isFlexible()) return
|
||||
|
||||
val lowerBound = expectedType.flexibility().lowerBound
|
||||
val upperBound = expectedType.flexibility().upperBound
|
||||
|
||||
// Use site variance projection is always the same for flexible types
|
||||
if (lowerBound.constructor == upperBound.constructor) return
|
||||
// Anything is acceptable for raw types
|
||||
if (expectedType.getCapability<RawTypeTag>() != null) return
|
||||
|
||||
val correspondingSubType = TypeCheckingProcedure.findCorrespondingSupertype(expressionTypeWithSmartCast, lowerBound) ?: return
|
||||
|
||||
assert(lowerBound.arguments.size() == upperBound.arguments.size()) {
|
||||
"Different arguments count in flexible bounds: " +
|
||||
"($lowerBound(${lowerBound.arguments.size()})..$upperBound(${upperBound.arguments.size()})"
|
||||
}
|
||||
|
||||
assert(lowerBound.arguments.size() == correspondingSubType.arguments.size()) {
|
||||
"Different arguments count in corresponding subtype and supertype: " +
|
||||
"($lowerBound(${lowerBound.arguments.size()})..$correspondingSubType(${correspondingSubType.arguments.size()})"
|
||||
}
|
||||
|
||||
|
||||
val lowerParameters = lowerBound.constructor.parameters
|
||||
val upperParameters = upperBound.constructor.parameters
|
||||
val lowerArguments = lowerBound.arguments
|
||||
|
||||
correspondingSubType.arguments.indices.forEach {
|
||||
index ->
|
||||
val lowerArgument = lowerArguments[index]
|
||||
// Currently we don't have flexible types with different constructors with contravariant arguments
|
||||
// So check just covariant case
|
||||
if (lowerParameters[index].variance == Variance.INVARIANT
|
||||
&& upperParameters[index].variance == Variance.OUT_VARIANCE
|
||||
&& lowerArgument.projectionKind != Variance.OUT_VARIANCE
|
||||
&& !JetTypeChecker.DEFAULT.equalTypes(correspondingSubType.arguments[index].type, lowerArgument.type)
|
||||
) {
|
||||
c.trace.report(ErrorsJvm.JAVA_TYPE_MISMATCH.on(expression, expressionTypeWithSmartCast, expectedType))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun checkReceiver(
|
||||
receiverParameter: ReceiverParameterDescriptor,
|
||||
receiverArgument: ReceiverValue,
|
||||
safeAccess: Boolean, c: CallResolutionContext<*>) { }
|
||||
}
|
||||
+3
-1
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.jvm.RuntimeAssertionsTypeChecker
|
||||
import org.jetbrains.kotlin.lexer.JetTokens
|
||||
import org.jetbrains.kotlin.load.java.lazy.types.RawTypeTag
|
||||
import org.jetbrains.kotlin.load.java.lazy.types.isMarkedNotNull
|
||||
import org.jetbrains.kotlin.load.java.lazy.types.isMarkedNullable
|
||||
import org.jetbrains.kotlin.load.kotlin.JavaAnnotationCallChecker
|
||||
@@ -83,7 +84,8 @@ public object JvmPlatformConfigurator : PlatformConfigurator(
|
||||
|
||||
additionalTypeCheckers = listOf(
|
||||
JavaNullabilityWarningsChecker(),
|
||||
RuntimeAssertionsTypeChecker
|
||||
RuntimeAssertionsTypeChecker,
|
||||
JavaGenericVarianceViolationTypeChecker
|
||||
),
|
||||
|
||||
additionalSymbolUsageValidators = listOf(),
|
||||
|
||||
Reference in New Issue
Block a user