Add warnings for jsr305 nullable annotations
#KT-19115 Fixed
This commit is contained in:
+5
-5
@@ -156,18 +156,18 @@ class K2JVMCompilerArguments : CommonCompilerArguments() {
|
||||
|
||||
@Argument(
|
||||
value = "-Xjsr305-annotations",
|
||||
valueDescription = "{ignore|enable}",
|
||||
description = "Specify global behavior for JSR-305 nullability annotations: ignore, or treat as other supported nullability annotations"
|
||||
valueDescription = "{ignore|enable|warn}",
|
||||
description = "Specify global behavior for JSR-305 nullability annotations: ignore, treat as other supported nullability annotations, or report a warning"
|
||||
)
|
||||
var jsr305GlobalReportLevel: String? by FreezableVar(Jsr305State.DEFAULT.description)
|
||||
var jsr305GlobalState: String? by FreezableVar(Jsr305State.DEFAULT.description)
|
||||
|
||||
// Paths to output directories for friend modules.
|
||||
var friendPaths: Array<String>? by FreezableVar(null)
|
||||
|
||||
override fun configureAnalysisFlags(): MutableMap<AnalysisFlag<*>, Any> {
|
||||
val result = super.configureAnalysisFlags()
|
||||
Jsr305State.findByDescription(jsr305GlobalReportLevel)?.let {
|
||||
result.put(AnalysisFlag.loadJsr305Annotations, it)
|
||||
Jsr305State.findByDescription(jsr305GlobalState)?.let {
|
||||
result.put(AnalysisFlag.jsr305GlobalState, it)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -103,12 +103,7 @@ fun createContainerForLazyResolveWithJava(
|
||||
|
||||
useInstance(languageVersionSettings)
|
||||
|
||||
if (languageVersionSettings.getFlag(AnalysisFlag.loadJsr305Annotations).shouldReportError) {
|
||||
useImpl<AnnotationTypeQualifierResolverImpl>()
|
||||
}
|
||||
else {
|
||||
useInstance(AnnotationTypeQualifierResolver.Empty)
|
||||
}
|
||||
useInstance(languageVersionSettings.getFlag(AnalysisFlag.jsr305GlobalState))
|
||||
|
||||
if (useBuiltInsProvider) {
|
||||
useInstance((moduleContext.module.builtIns as JvmBuiltIns).settings)
|
||||
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.checkers
|
||||
|
||||
import org.jetbrains.kotlin.cfg.WhenChecker
|
||||
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtPostfixExpression
|
||||
import org.jetbrains.kotlin.psi.KtWhenExpression
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
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.calls.smartcasts.DataFlowInfo
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.expressions.SenselessComparisonChecker
|
||||
|
||||
class JavaNullabilityChecker : AdditionalTypeChecker {
|
||||
|
||||
override fun checkType(expression: KtExpression, expressionType: KotlinType, expressionTypeWithSmartCast: KotlinType, c: ResolutionContext<*>) {
|
||||
doCheckType(
|
||||
expressionType,
|
||||
c.expectedType,
|
||||
DataFlowValueFactory.createDataFlowValue(expression, expressionType, c),
|
||||
c.dataFlowInfo
|
||||
) { expectedMustNotBeNull, actualMayBeNull ->
|
||||
c.trace.report(ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS.on(expression, expectedMustNotBeNull, actualMayBeNull))
|
||||
}
|
||||
|
||||
when (expression) {
|
||||
is KtWhenExpression ->
|
||||
if (expression.elseExpression == null) {
|
||||
// Check for conditionally-exhaustive when on platform enums, see KT-6399
|
||||
val subjectExpression = expression.subjectExpression ?: return
|
||||
val type = c.trace.getType(subjectExpression) ?: return
|
||||
if (type.isFlexible() && TypeUtils.isNullableType(type.asFlexibleType().upperBound)) {
|
||||
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(subjectExpression, type, c)
|
||||
val dataFlowInfo = c.trace[BindingContext.EXPRESSION_TYPE_INFO, subjectExpression]?.dataFlowInfo
|
||||
if (dataFlowInfo != null && !dataFlowInfo.getStableNullability(dataFlowValue).canBeNull()) {
|
||||
return
|
||||
}
|
||||
|
||||
val enumClassDescriptor = WhenChecker.getClassDescriptorOfTypeIfEnum(type) ?: return
|
||||
val context = c.trace.bindingContext
|
||||
if (WhenChecker.getEnumMissingCases(expression, context, enumClassDescriptor).isEmpty()
|
||||
&& !WhenChecker.containsNullCase(expression, context)) {
|
||||
|
||||
c.trace.report(ErrorsJvm.WHEN_ENUM_CAN_BE_NULL_IN_JAVA.on(expression.subjectExpression!!))
|
||||
}
|
||||
}
|
||||
}
|
||||
is KtPostfixExpression ->
|
||||
if (expression.operationToken == KtTokens.EXCLEXCL) {
|
||||
val baseExpression = expression.baseExpression ?: return
|
||||
val baseExpressionType = c.trace.getType(baseExpression) ?: return
|
||||
doIfNotNull(
|
||||
DataFlowValueFactory.createDataFlowValue(baseExpression, baseExpressionType, c), c
|
||||
) {
|
||||
c.trace.report(Errors.UNNECESSARY_NOT_NULL_ASSERTION.on(expression.operationReference, baseExpressionType))
|
||||
}
|
||||
}
|
||||
is KtBinaryExpression ->
|
||||
when (expression.operationToken) {
|
||||
KtTokens.EQEQ,
|
||||
KtTokens.EXCLEQ,
|
||||
KtTokens.EQEQEQ,
|
||||
KtTokens.EXCLEQEQEQ -> {
|
||||
if (expression.left != null && expression.right != null) {
|
||||
SenselessComparisonChecker.checkSenselessComparisonWithNull(
|
||||
expression, expression.left!!, expression.right!!, c,
|
||||
{ c.trace.getType(it) },
|
||||
{ value ->
|
||||
doIfNotNull(value, c) { Nullability.NOT_NULL } ?: Nullability.UNKNOWN
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun checkReceiver(receiverParameter: ReceiverParameterDescriptor, receiverArgument: ReceiverValue, safeAccess: Boolean, c: CallResolutionContext<*>) {
|
||||
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverArgument, c)
|
||||
if (safeAccess) {
|
||||
doIfNotNull(dataFlowValue, c) {
|
||||
c.trace.report(Errors.UNNECESSARY_SAFE_CALL.on(c.call.callOperationNode!!.psi, receiverArgument.type))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
doCheckType(
|
||||
receiverArgument.type,
|
||||
receiverParameter.type,
|
||||
dataFlowValue,
|
||||
c.dataFlowInfo
|
||||
) { expectedMustNotBeNull,
|
||||
actualMayBeNull ->
|
||||
val reportOn = (receiverArgument as? ExpressionReceiver)?.expression ?: (c.call.calleeExpression ?: c.call.callElement)
|
||||
c.trace.report(ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS.on(
|
||||
reportOn, expectedMustNotBeNull, actualMayBeNull
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
private fun doCheckType(
|
||||
expressionType: KotlinType,
|
||||
expectedType: KotlinType,
|
||||
dataFlowValue: DataFlowValue,
|
||||
dataFlowInfo: DataFlowInfo,
|
||||
reportWarning: (expectedMustNotBeNull: ErrorsJvm.NullabilityInformationSource, actualMayBeNull: ErrorsJvm.NullabilityInformationSource) -> Unit
|
||||
) {
|
||||
if (TypeUtils.noExpectedType(expectedType)) {
|
||||
return
|
||||
}
|
||||
|
||||
val expectedMustNotBeNull = expectedType.mustNotBeNull()
|
||||
if (dataFlowInfo.getStableNullability(dataFlowValue) == Nullability.NOT_NULL) {
|
||||
return
|
||||
}
|
||||
|
||||
val actualMayBeNull = expressionType.mayBeNull()
|
||||
if (expectedMustNotBeNull == ErrorsJvm.NullabilityInformationSource.KOTLIN && actualMayBeNull == ErrorsJvm.NullabilityInformationSource.KOTLIN) {
|
||||
// a type mismatch error will be reported elsewhere
|
||||
return
|
||||
}
|
||||
|
||||
if (expectedMustNotBeNull != null && actualMayBeNull != null) {
|
||||
reportWarning(expectedMustNotBeNull, actualMayBeNull)
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T : Any> doIfNotNull(dataFlowValue: DataFlowValue, c: ResolutionContext<*>, body: () -> T): T? =
|
||||
if (c.dataFlowInfo.getStableNullability(dataFlowValue).canBeNull() &&
|
||||
dataFlowValue.type.mustNotBeNull() == ErrorsJvm.NullabilityInformationSource.JAVA)
|
||||
body()
|
||||
else null
|
||||
|
||||
private fun KotlinType.mustNotBeNull(): ErrorsJvm.NullabilityInformationSource? = when {
|
||||
!isError && !isFlexible() && !TypeUtils.acceptsNullable(this) -> ErrorsJvm.NullabilityInformationSource.KOTLIN
|
||||
isFlexible() && !TypeUtils.acceptsNullable(asFlexibleType().upperBound) -> ErrorsJvm.NullabilityInformationSource.KOTLIN
|
||||
this is TypeWithEnhancement && enhancement.mustNotBeNull() != null -> ErrorsJvm.NullabilityInformationSource.JAVA
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun KotlinType.mayBeNull(): ErrorsJvm.NullabilityInformationSource? = when {
|
||||
!isError && !isFlexible() && TypeUtils.acceptsNullable(this) -> ErrorsJvm.NullabilityInformationSource.KOTLIN
|
||||
isFlexible() && TypeUtils.acceptsNullable(asFlexibleType().lowerBound) -> ErrorsJvm.NullabilityInformationSource.KOTLIN
|
||||
this is TypeWithEnhancement && enhancement.mayBeNull() != null -> ErrorsJvm.NullabilityInformationSource.JAVA
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.checkers
|
||||
|
||||
import org.jetbrains.kotlin.cfg.WhenChecker
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtWhenExpression
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.AdditionalTypeChecker
|
||||
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.asFlexibleType
|
||||
import org.jetbrains.kotlin.types.isFlexible
|
||||
|
||||
class WhenByPlatformEnumChecker : AdditionalTypeChecker {
|
||||
|
||||
override fun checkType(expression: KtExpression, expressionType: KotlinType, expressionTypeWithSmartCast: KotlinType, c: ResolutionContext<*>) {
|
||||
if (expression is KtWhenExpression && expression.elseExpression == null) {
|
||||
// Check for conditionally-exhaustive when on platform enums, see KT-6399
|
||||
val subjectExpression = expression.subjectExpression ?: return
|
||||
val type = c.trace.getType(subjectExpression) ?: return
|
||||
if (type.isFlexible() && TypeUtils.isNullableType(type.asFlexibleType().upperBound)) {
|
||||
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(subjectExpression, type, c)
|
||||
val dataFlowInfo = c.trace[BindingContext.EXPRESSION_TYPE_INFO, subjectExpression]?.dataFlowInfo
|
||||
if (dataFlowInfo != null && !dataFlowInfo.getStableNullability(dataFlowValue).canBeNull()) {
|
||||
return
|
||||
}
|
||||
|
||||
val enumClassDescriptor = WhenChecker.getClassDescriptorOfTypeIfEnum(type) ?: return
|
||||
val context = c.trace.bindingContext
|
||||
if (WhenChecker.getEnumMissingCases(expression, context, enumClassDescriptor).isEmpty()
|
||||
&& !WhenChecker.containsNullCase(expression, context)) {
|
||||
|
||||
c.trace.report(ErrorsJvm.WHEN_ENUM_CAN_BE_NULL_IN_JAVA.on(expression.subjectExpression!!))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+2
@@ -79,6 +79,8 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension {
|
||||
MAP.put(INTERFACE_CANT_CALL_DEFAULT_METHOD_VIA_SUPER, "Interfaces can't call Java default methods via super");
|
||||
MAP.put(SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC, "Using non-JVM static members protected in the superclass companion is unsupported yet");
|
||||
|
||||
MAP.put(ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS,
|
||||
"Expected type does not accept nulls in {0}, but the value may be null in {1}", Renderers.TO_STRING, Renderers.TO_STRING);
|
||||
MAP.put(WHEN_ENUM_CAN_BE_NULL_IN_JAVA, "Enum argument can be null in Java, but exhaustive when contains no null branch");
|
||||
|
||||
MAP.put(JAVA_CLASS_ON_COMPANION,
|
||||
|
||||
+22
@@ -17,11 +17,13 @@
|
||||
package org.jetbrains.kotlin.resolve.jvm.diagnostics;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.kotlin.diagnostics.*;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.psi.KtAnnotationEntry;
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration;
|
||||
import org.jetbrains.kotlin.psi.KtElement;
|
||||
import org.jetbrains.kotlin.psi.KtExpression;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
|
||||
@@ -98,6 +100,26 @@ public interface ErrorsJvm {
|
||||
DiagnosticFactory0<PsiElement> JAVA_MODULE_DOES_NOT_READ_UNNAMED_MODULE = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory2<PsiElement, String, String> JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE = DiagnosticFactory2.create(ERROR);
|
||||
|
||||
enum NullabilityInformationSource {
|
||||
KOTLIN {
|
||||
@NotNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Kotlin";
|
||||
}
|
||||
},
|
||||
JAVA {
|
||||
@NotNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Java";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DiagnosticFactory2<KtElement, NullabilityInformationSource, NullabilityInformationSource> NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS
|
||||
= DiagnosticFactory2.create(WARNING);
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
Object _initializer = new Object() {
|
||||
{
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ object JvmPlatformConfigurator : PlatformConfigurator(
|
||||
),
|
||||
|
||||
additionalTypeCheckers = listOf(
|
||||
WhenByPlatformEnumChecker(),
|
||||
JavaNullabilityChecker(),
|
||||
RuntimeAssertionsTypeChecker,
|
||||
JavaGenericVarianceViolationTypeChecker,
|
||||
JavaTypeAccessibilityChecker()
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.jetbrains.kotlin.resolve.calls.callResolverUtil.getErasedReceiverType
|
||||
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isInvokeCallOnExpressionWithBothReceivers
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.isExplicitSafeCall
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.AdditionalTypeChecker
|
||||
import org.jetbrains.kotlin.resolve.calls.context.*
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.SubstitutionFilteringInternalResolveAnnotations
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatchStatus
|
||||
@@ -60,6 +61,7 @@ class CandidateResolver(
|
||||
private val argumentTypeResolver: ArgumentTypeResolver,
|
||||
private val genericCandidateResolver: GenericCandidateResolver,
|
||||
private val reflectionTypes: ReflectionTypes,
|
||||
private val additionalTypeCheckers: Iterable<AdditionalTypeChecker>,
|
||||
private val smartCastManager: SmartCastManager
|
||||
) {
|
||||
fun <D : CallableDescriptor> performResolutionForCandidateCall(
|
||||
@@ -581,6 +583,8 @@ class CandidateResolver(
|
||||
return UNSAFE_CALL_ERROR
|
||||
}
|
||||
|
||||
additionalTypeCheckers.forEach { it.checkReceiver(receiverParameter, receiverArgument, safeAccess, this) }
|
||||
|
||||
return SUCCESS
|
||||
}
|
||||
|
||||
|
||||
+17
-1
@@ -16,10 +16,26 @@
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.checkers
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.resolve.calls.context.CallResolutionContext
|
||||
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
|
||||
interface AdditionalTypeChecker {
|
||||
fun checkType(expression: KtExpression, expressionType: KotlinType, expressionTypeWithSmartCast: KotlinType, c: ResolutionContext<*>)
|
||||
fun checkType(
|
||||
expression: KtExpression,
|
||||
expressionType: KotlinType,
|
||||
expressionTypeWithSmartCast: KotlinType,
|
||||
c: ResolutionContext<*>
|
||||
)
|
||||
|
||||
fun checkReceiver(
|
||||
receiverParameter: ReceiverParameterDescriptor,
|
||||
receiverArgument: ReceiverValue,
|
||||
safeAccess: Boolean,
|
||||
c: CallResolutionContext<*>
|
||||
) { }
|
||||
|
||||
}
|
||||
+2
-2
@@ -8,8 +8,8 @@ where advanced options include:
|
||||
-Xmultifile-parts-inherit Compile multifile classes as a hierarchy of parts and facade
|
||||
-Xmodule-path=<path> Paths where to find Java 9+ modules
|
||||
-Xjavac-arguments=<option[,]> Java compiler arguments
|
||||
-Xjsr305-annotations={ignore|enable}
|
||||
Specify global behavior for JSR-305 nullability annotations: ignore, or treat as other supported nullability annotations
|
||||
-Xjsr305-annotations={ignore|enable|warn}
|
||||
Specify global behavior for JSR-305 nullability annotations: ignore, treat as other supported nullability annotations, or report a warning
|
||||
-Xload-builtins-from-dependencies
|
||||
Load definitions of built-in declarations from module dependencies, instead of from the compiler
|
||||
-Xno-call-assertions Don't generate not-null assertion after each invocation of method returning not-null
|
||||
|
||||
+1
-1
@@ -80,7 +80,7 @@ public abstract class KotlinMultiFileTestWithJava<M, F> extends KtUsefulTestCase
|
||||
CollectionsKt.plus(Collections.singletonList(KotlinTestUtils.getAnnotationsJar()), getExtraClasspath()),
|
||||
isJavaSourceRootNeeded() ? Collections.singletonList(javaFilesDir) : Collections.emptyList()
|
||||
);
|
||||
configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, StandardScriptDefinition.INSTANCE);
|
||||
configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, StandardScriptDefinition.INSTANCE);
|
||||
if (isKotlinSourceRootNeeded()) {
|
||||
ContentRootsKt.addKotlinSourceRoot(configuration, kotlinSourceRoot.getPath());
|
||||
}
|
||||
|
||||
+11
-10
@@ -25,8 +25,17 @@ import java.io.File
|
||||
abstract class AbstractForeignAnnotationsNoAnnotationInClasspathTest : AbstractForeignAnnotationsTest() {
|
||||
private val compiledJavaPath = KotlinTestUtils.tmpDir("java-compiled-files")
|
||||
override fun getExtraClasspath(): List<File> {
|
||||
compileJavaFiles()
|
||||
return listOf(compiledJavaPath)
|
||||
val foreignAnnotations = createJarWithForeignAnnotations()
|
||||
val testAnnotations = compileTestAnnotations(foreignAnnotations)
|
||||
|
||||
val additionalClasspath = (foreignAnnotations + testAnnotations).map { it.path }
|
||||
CodegenTestUtil.compileJava(
|
||||
CodegenTestUtil.findJavaSourcesInDirectory(javaFilesDir),
|
||||
additionalClasspath, emptyList(),
|
||||
compiledJavaPath
|
||||
)
|
||||
|
||||
return listOf(compiledJavaPath) + testAnnotations
|
||||
}
|
||||
|
||||
override fun analyzeAndCheck(testDataFile: File, files: List<TestFile>) {
|
||||
@@ -39,12 +48,4 @@ abstract class AbstractForeignAnnotationsNoAnnotationInClasspathTest : AbstractF
|
||||
|
||||
private fun createJarWithForeignAnnotations(): List<File> =
|
||||
listOf(MockLibraryUtil.compileJvmLibraryToJar(annotationsPath, "foreign-annotations"))
|
||||
|
||||
private fun compileJavaFiles() {
|
||||
CodegenTestUtil.compileJava(
|
||||
CodegenTestUtil.findJavaSourcesInDirectory(javaFilesDir),
|
||||
createJarWithForeignAnnotations().map { it.path }, emptyList(),
|
||||
compiledJavaPath
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,20 +17,40 @@
|
||||
package org.jetbrains.kotlin.checkers
|
||||
|
||||
import org.jetbrains.kotlin.config.*
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import org.jetbrains.kotlin.test.MockLibraryUtil
|
||||
import org.jetbrains.kotlin.utils.Jsr305State
|
||||
import java.io.File
|
||||
|
||||
val FOREIGN_ANNOTATIONS_SOURCES_PATH = "compiler/testData/foreignAnnotations/annotations"
|
||||
val TEST_ANNOTATIONS_SOURCE_PATH = "compiler/testData/foreignAnnotations/testAnnotations"
|
||||
|
||||
abstract class AbstractForeignAnnotationsTest : AbstractDiagnosticsWithFullJdkTest() {
|
||||
override fun getExtraClasspath(): List<File> =
|
||||
listOf(MockLibraryUtil.compileJvmLibraryToJar(annotationsPath, "foreign-annotations"))
|
||||
private val WARNING_FOR_JSR305_ANNOTATIONS_DIRECTIVE = "WARNING_FOR_JSR305_ANNOTATIONS"
|
||||
|
||||
override fun getExtraClasspath(): List<File> {
|
||||
val foreignAnnotations = listOf(MockLibraryUtil.compileJvmLibraryToJar(annotationsPath, "foreign-annotations"))
|
||||
return foreignAnnotations + compileTestAnnotations(foreignAnnotations)
|
||||
}
|
||||
|
||||
protected fun compileTestAnnotations(extraClassPath: List<File>): List<File> =
|
||||
listOf(MockLibraryUtil.compileJvmLibraryToJar(
|
||||
TEST_ANNOTATIONS_SOURCE_PATH,
|
||||
"test-foreign-annotations",
|
||||
extraClasspath = extraClassPath.map { it.path }
|
||||
))
|
||||
|
||||
open protected val annotationsPath: String
|
||||
get() = FOREIGN_ANNOTATIONS_SOURCES_PATH
|
||||
|
||||
override fun loadLanguageVersionSettings(module: List<TestFile>): LanguageVersionSettings =
|
||||
LanguageVersionSettingsImpl(LanguageVersion.LATEST_STABLE, ApiVersion.LATEST_STABLE,
|
||||
mapOf(AnalysisFlag.loadJsr305Annotations to Jsr305State.ENABLE))
|
||||
override fun loadLanguageVersionSettings(module: List<TestFile>): LanguageVersionSettings {
|
||||
val hasWarningDirective = module.any {
|
||||
InTextDirectivesUtils.isDirectiveDefined(it.expectedText, WARNING_FOR_JSR305_ANNOTATIONS_DIRECTIVE)
|
||||
}
|
||||
|
||||
val jsr305State = if (hasWarningDirective) Jsr305State.WARN else Jsr305State.ENABLE
|
||||
return LanguageVersionSettingsImpl(LanguageVersion.LATEST_STABLE, ApiVersion.LATEST_STABLE,
|
||||
mapOf(AnalysisFlag.jsr305GlobalState to jsr305State)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ class LoadJavaPackageAnnotationsTest : KtUsefulTestCase() {
|
||||
put(JVMConfigurationKeys.USE_JAVAC, true)
|
||||
}
|
||||
languageVersionSettings = LanguageVersionSettingsImpl(
|
||||
LanguageVersion.LATEST_STABLE, ApiVersion.LATEST_STABLE, mapOf(AnalysisFlag.loadJsr305Annotations to Jsr305State.ENABLE)
|
||||
LanguageVersion.LATEST_STABLE, ApiVersion.LATEST_STABLE, mapOf(AnalysisFlag.jsr305GlobalState to Jsr305State.ENABLE)
|
||||
)
|
||||
configurator(this)
|
||||
}
|
||||
|
||||
+1
-1
@@ -96,7 +96,7 @@ class TypeQualifierAnnotationResolverTest : KtUsefulTestCase() {
|
||||
listOf(File(TEST_DATA_PATH))
|
||||
).apply {
|
||||
languageVersionSettings = LanguageVersionSettingsImpl(
|
||||
LanguageVersion.LATEST_STABLE, ApiVersion.LATEST_STABLE, mapOf(AnalysisFlag.loadJsr305Annotations to Jsr305State.ENABLE)
|
||||
LanguageVersion.LATEST_STABLE, ApiVersion.LATEST_STABLE, mapOf(AnalysisFlag.jsr305GlobalState to Jsr305State.ENABLE)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,6 @@ class AnalysisFlag<out T> internal constructor(
|
||||
val multiPlatformDoNotCheckImpl by Flag.Boolean
|
||||
|
||||
@JvmStatic
|
||||
val loadJsr305Annotations by Flag.Jsr305StateIgnoreByDefault
|
||||
val jsr305GlobalState by Flag.Jsr305StateIgnoreByDefault
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
-17
@@ -19,8 +19,6 @@ package org.jetbrains.kotlin.load.java
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
import org.jetbrains.kotlin.load.java.AnnotationTypeQualifierResolver.QualifierApplicabilityType
|
||||
import org.jetbrains.kotlin.load.java.AnnotationTypeQualifierResolver.TypeQualifierWithApplicability
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.resolve.constants.ArrayValue
|
||||
import org.jetbrains.kotlin.resolve.constants.ConstantValue
|
||||
@@ -28,17 +26,14 @@ import org.jetbrains.kotlin.resolve.constants.EnumValue
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
import org.jetbrains.kotlin.utils.Jsr305State
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
|
||||
|
||||
private val TYPE_QUALIFIER_NICKNAME_FQNAME = FqName("javax.annotation.meta.TypeQualifierNickname")
|
||||
private val TYPE_QUALIFIER_FQNAME = FqName("javax.annotation.meta.TypeQualifier")
|
||||
private val TYPE_QUALIFIER_DEFAULT_FQNAME = FqName("javax.annotation.meta.TypeQualifierDefault")
|
||||
|
||||
interface AnnotationTypeQualifierResolver {
|
||||
fun resolveTypeQualifierAnnotation(annotationDescriptor: AnnotationDescriptor): AnnotationDescriptor?
|
||||
|
||||
fun resolveTypeQualifierDefaultAnnotation(annotationDescriptor: AnnotationDescriptor): TypeQualifierWithApplicability?
|
||||
|
||||
class AnnotationTypeQualifierResolver(storageManager: StorageManager, val jsr305State: Jsr305State) {
|
||||
enum class QualifierApplicabilityType {
|
||||
METHOD_RETURN_TYPE, VALUE_PARAMETER, FIELD, TYPE_USE
|
||||
}
|
||||
@@ -53,14 +48,6 @@ interface AnnotationTypeQualifierResolver {
|
||||
operator fun component2() = QualifierApplicabilityType.values().filter(this::isApplicableTo)
|
||||
}
|
||||
|
||||
object Empty : AnnotationTypeQualifierResolver {
|
||||
override fun resolveTypeQualifierAnnotation(annotationDescriptor: AnnotationDescriptor): AnnotationDescriptor? = null
|
||||
|
||||
override fun resolveTypeQualifierDefaultAnnotation(annotationDescriptor: AnnotationDescriptor): TypeQualifierWithApplicability? = null
|
||||
}
|
||||
}
|
||||
|
||||
class AnnotationTypeQualifierResolverImpl(storageManager: StorageManager) : AnnotationTypeQualifierResolver {
|
||||
private val resolvedNicknames =
|
||||
storageManager.createMemoizedFunctionWithNullableValues(this::computeTypeQualifierNickname)
|
||||
|
||||
@@ -76,14 +63,22 @@ class AnnotationTypeQualifierResolverImpl(storageManager: StorageManager) : Anno
|
||||
return resolvedNicknames(classDescriptor)
|
||||
}
|
||||
|
||||
override fun resolveTypeQualifierAnnotation(annotationDescriptor: AnnotationDescriptor): AnnotationDescriptor? {
|
||||
fun resolveTypeQualifierAnnotation(annotationDescriptor: AnnotationDescriptor): AnnotationDescriptor? {
|
||||
if (jsr305State.isIgnored()) {
|
||||
return null
|
||||
}
|
||||
|
||||
val annotationClass = annotationDescriptor.annotationClass ?: return null
|
||||
if (annotationClass.isAnnotatedWithTypeQualifier) return annotationDescriptor
|
||||
|
||||
return resolveTypeQualifierNickname(annotationClass)
|
||||
}
|
||||
|
||||
override fun resolveTypeQualifierDefaultAnnotation(annotationDescriptor: AnnotationDescriptor): TypeQualifierWithApplicability? {
|
||||
fun resolveTypeQualifierDefaultAnnotation(annotationDescriptor: AnnotationDescriptor): TypeQualifierWithApplicability? {
|
||||
if (jsr305State.isIgnored()) {
|
||||
return null
|
||||
}
|
||||
|
||||
val typeQualifierDefaultAnnotatedClass =
|
||||
annotationDescriptor.annotationClass?.takeIf { it.annotations.hasAnnotation(TYPE_QUALIFIER_DEFAULT_FQNAME) }
|
||||
?: return null
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.load.java.sources.JavaSourceElementFactory
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameterListOwner
|
||||
import org.jetbrains.kotlin.load.java.typeEnhancement.JavaTypeQualifiers
|
||||
import org.jetbrains.kotlin.load.java.typeEnhancement.NullabilityQualifier
|
||||
import org.jetbrains.kotlin.load.java.typeEnhancement.NullabilityQualifierWithMigrationStatus
|
||||
import org.jetbrains.kotlin.load.java.typeEnhancement.SignatureEnhancement
|
||||
import org.jetbrains.kotlin.load.kotlin.DeserializedDescriptorResolver
|
||||
import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder
|
||||
@@ -69,7 +70,7 @@ class JavaResolverComponents(
|
||||
)
|
||||
}
|
||||
|
||||
private typealias QualifierByApplicabilityType = EnumMap<AnnotationTypeQualifierResolver.QualifierApplicabilityType, NullabilityQualifier?>
|
||||
private typealias QualifierByApplicabilityType = EnumMap<AnnotationTypeQualifierResolver.QualifierApplicabilityType, NullabilityQualifierWithMigrationStatus?>
|
||||
|
||||
class JavaTypeQualifiersByElementType(
|
||||
internal val nullabilityQualifiers: QualifierByApplicabilityType
|
||||
@@ -80,7 +81,7 @@ class JavaTypeQualifiersByElementType(
|
||||
(
|
||||
nullabilityQualifiers[applicabilityType]
|
||||
?: nullabilityQualifiers[AnnotationTypeQualifierResolver.QualifierApplicabilityType.TYPE_USE]
|
||||
)?.let { JavaTypeQualifiers(it, null, isNotNullTypeParameter = false) }
|
||||
)?.let { JavaTypeQualifiers(it.qualifier, null, isNotNullTypeParameter = false, isNullabilityQualifierForWarning = it.isForWarningOnly) }
|
||||
}
|
||||
|
||||
class LazyJavaResolverContext internal constructor(
|
||||
@@ -121,9 +122,10 @@ fun LazyJavaResolverContext.computeNewDefaultTypeQualifiers(
|
||||
?: QualifierByApplicabilityType(AnnotationTypeQualifierResolver.QualifierApplicabilityType::class.java)
|
||||
|
||||
var wasUpdate = false
|
||||
val isForWarning = components.annotationTypeQualifierResolver.jsr305State.isWarning()
|
||||
for ((nullability, applicableTo) in nullabilityQualifiersWithApplicability) {
|
||||
for (applicabilityType in applicableTo) {
|
||||
nullabilityQualifiersByType[applicabilityType] = nullability
|
||||
nullabilityQualifiersByType[applicabilityType] = NullabilityQualifierWithMigrationStatus(nullability, isForWarning)
|
||||
wasUpdate = true
|
||||
}
|
||||
}
|
||||
@@ -140,9 +142,9 @@ private fun LazyJavaResolverContext.extractDefaultNullabilityQualifier(
|
||||
components.annotationTypeQualifierResolver.resolveTypeQualifierDefaultAnnotation(annotationDescriptor)
|
||||
?: return null
|
||||
|
||||
val nullability = components.signatureEnhancement.extractNullability(typeQualifier) ?: return null
|
||||
val nullabilityQualifier = components.signatureEnhancement.extractNullability(typeQualifier)?.qualifier ?: return null
|
||||
|
||||
return NullabilityQualifierWithApplicability(nullability, applicability)
|
||||
return NullabilityQualifierWithApplicability(nullabilityQualifier, applicability)
|
||||
}
|
||||
|
||||
data class NullabilityQualifierWithApplicability(
|
||||
|
||||
+1
-2
@@ -20,7 +20,6 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.SourceElement
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.PackageFragmentDescriptorImpl
|
||||
import org.jetbrains.kotlin.load.java.AnnotationTypeQualifierResolver
|
||||
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
|
||||
import org.jetbrains.kotlin.load.java.lazy.childForClassOrPackage
|
||||
import org.jetbrains.kotlin.load.java.lazy.resolveAnnotations
|
||||
@@ -56,7 +55,7 @@ class LazyJavaPackageFragment(
|
||||
|
||||
override val annotations =
|
||||
// Do not resolve package annotations if JSR-305 is disabled
|
||||
if (c.components.annotationTypeQualifierResolver == AnnotationTypeQualifierResolver.Empty) Annotations.EMPTY
|
||||
if (c.components.annotationTypeQualifierResolver.jsr305State.isIgnored()) Annotations.EMPTY
|
||||
else c.resolveAnnotations(jPackage)
|
||||
|
||||
internal fun getSubPackageFqNames(): List<FqName> = subPackages()
|
||||
|
||||
+82
-57
@@ -35,45 +35,56 @@ import org.jetbrains.kotlin.load.kotlin.SignatureBuildingComponents
|
||||
import org.jetbrains.kotlin.load.kotlin.computeJvmDescriptor
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.firstArgumentValue
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.asFlexibleType
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||
import org.jetbrains.kotlin.types.isFlexible
|
||||
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import java.util.*
|
||||
|
||||
data class NullabilityQualifierWithMigrationStatus(
|
||||
val qualifier: NullabilityQualifier,
|
||||
val isForWarningOnly: Boolean = false
|
||||
)
|
||||
|
||||
class SignatureEnhancement(private val annotationTypeQualifierResolver: AnnotationTypeQualifierResolver) {
|
||||
|
||||
fun extractNullability(annotationDescriptor: AnnotationDescriptor): NullabilityQualifier? {
|
||||
val annotationFqName = annotationDescriptor.fqName ?: return null
|
||||
when (annotationFqName) {
|
||||
in NULLABLE_ANNOTATIONS -> return NullabilityQualifier.NULLABLE
|
||||
in NOT_NULL_ANNOTATIONS -> return NullabilityQualifier.NOT_NULL
|
||||
}
|
||||
|
||||
val typeQualifier =
|
||||
when {
|
||||
annotationFqName == JAVAX_NONNULL_ANNOTATION -> annotationDescriptor
|
||||
else -> annotationTypeQualifierResolver.resolveTypeQualifierAnnotation(annotationDescriptor)
|
||||
?.takeIf { it.fqName == JAVAX_NONNULL_ANNOTATION }
|
||||
} ?: return null
|
||||
|
||||
val enumEntryDescriptor =
|
||||
typeQualifier.allValueArguments.values.singleOrNull()?.value
|
||||
// if no argument is specified, use default value: NOT_NULL
|
||||
?: return NullabilityQualifier.NOT_NULL
|
||||
private fun AnnotationDescriptor.extractNullabilityTypeFromArgument(): NullabilityQualifierWithMigrationStatus? {
|
||||
val enumEntryDescriptor = firstArgumentValue()
|
||||
// if no argument is specified, use default value: NOT_NULL
|
||||
?: return NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL)
|
||||
|
||||
if (enumEntryDescriptor !is ClassDescriptor) return null
|
||||
|
||||
return when (enumEntryDescriptor.name.asString()) {
|
||||
"ALWAYS" -> NullabilityQualifier.NOT_NULL
|
||||
"MAYBE" -> NullabilityQualifier.NULLABLE
|
||||
"ALWAYS" -> NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL)
|
||||
"MAYBE" -> NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NULLABLE)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun extractNullability(annotationDescriptor: AnnotationDescriptor): NullabilityQualifierWithMigrationStatus? {
|
||||
val annotationFqName = annotationDescriptor.fqName ?: return null
|
||||
|
||||
return when (annotationFqName) {
|
||||
in NULLABLE_ANNOTATIONS -> NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NULLABLE)
|
||||
in NOT_NULL_ANNOTATIONS -> NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL)
|
||||
JAVAX_NONNULL_ANNOTATION -> annotationDescriptor.extractNullabilityTypeFromArgument()
|
||||
else -> {
|
||||
val forWarning = annotationTypeQualifierResolver.jsr305State.isWarning()
|
||||
|
||||
annotationTypeQualifierResolver
|
||||
.resolveTypeQualifierAnnotation(annotationDescriptor)
|
||||
?.takeIf { it.fqName == JAVAX_NONNULL_ANNOTATION }
|
||||
?.extractNullabilityTypeFromArgument()
|
||||
?.copy(isForWarningOnly = forWarning)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun <D : CallableMemberDescriptor> enhanceSignatures(c: LazyJavaResolverContext, platformSignatures: Collection<D>): Collection<D> {
|
||||
return platformSignatures.map {
|
||||
@@ -217,12 +228,17 @@ class SignatureEnhancement(private val annotationTypeQualifierResolver: Annotati
|
||||
fun <T: Any> uniqueNotNull(x: T?, y: T?) = if (x == null || y == null || x == y) x ?: y else null
|
||||
|
||||
val defaultTypeQualifier = defaultTopLevelQualifiers?.takeIf { isHeadTypeConstructor }
|
||||
val nullability =
|
||||
val nullabilityInfo =
|
||||
composedAnnotation.extractNullability()
|
||||
?: defaultTypeQualifier?.nullability
|
||||
?: defaultTypeQualifier?.nullability?.let {
|
||||
NullabilityQualifierWithMigrationStatus(
|
||||
defaultTypeQualifier.nullability,
|
||||
defaultTypeQualifier.isNullabilityQualifierForWarning
|
||||
)
|
||||
}
|
||||
|
||||
return JavaTypeQualifiers(
|
||||
nullability,
|
||||
nullabilityInfo?.qualifier,
|
||||
uniqueNotNull(
|
||||
READ_ONLY_ANNOTATIONS.ifPresent(
|
||||
MutabilityQualifier.READ_ONLY
|
||||
@@ -231,11 +247,12 @@ class SignatureEnhancement(private val annotationTypeQualifierResolver: Annotati
|
||||
MutabilityQualifier.MUTABLE
|
||||
)
|
||||
),
|
||||
isNotNullTypeParameter = nullability == NullabilityQualifier.NOT_NULL && isTypeParameter()
|
||||
isNotNullTypeParameter = nullabilityInfo?.qualifier == NullabilityQualifier.NOT_NULL && isTypeParameter(),
|
||||
isNullabilityQualifierForWarning = nullabilityInfo?.isForWarningOnly == true
|
||||
)
|
||||
}
|
||||
|
||||
private fun Annotations.extractNullability(): NullabilityQualifier? =
|
||||
private fun Annotations.extractNullability(): NullabilityQualifierWithMigrationStatus? =
|
||||
this.firstNotNullResult(this@SignatureEnhancement::extractNullability)
|
||||
|
||||
private fun computeIndexedQualifiersForOverride(): (Int) -> JavaTypeQualifiers {
|
||||
@@ -288,49 +305,57 @@ class SignatureEnhancement(private val annotationTypeQualifierResolver: Annotati
|
||||
fromSupertypes: Collection<KotlinType>, isCovariant: Boolean,
|
||||
isHeadTypeConstructor: Boolean
|
||||
): JavaTypeQualifiers {
|
||||
val nullabilityFromSupertypes = fromSupertypes.mapNotNull { it.extractQualifiers().nullability }.toSet()
|
||||
val mutabilityFromSupertypes = fromSupertypes.mapNotNull { it.extractQualifiers().mutability }.toSet()
|
||||
val superQualifiers = fromSupertypes.map { it.extractQualifiers() }
|
||||
val mutabilityFromSupertypes = superQualifiers.mapNotNull { it.mutability }.toSet()
|
||||
val nullabilityFromSupertypes = superQualifiers.mapNotNull { it.nullability }.toSet()
|
||||
val nullabilityFromSupertypesWithWarning = fromSupertypes
|
||||
.mapNotNull { it.unwrapEnhancement().extractQualifiers().nullability }
|
||||
.toSet()
|
||||
.takeIf { it != nullabilityFromSupertypes }
|
||||
|
||||
val own = extractQualifiersFromAnnotations(isHeadTypeConstructor)
|
||||
val isAnyNonNullTypeParameter = own.isNotNullTypeParameter || superQualifiers.any { it.isNotNullTypeParameter }
|
||||
|
||||
val isAnyNonNullTypeParameter = own.isNotNullTypeParameter || fromSupertypes.any { it.extractQualifiers().isNotNullTypeParameter }
|
||||
|
||||
fun createJavaTypeQualifiers(nullability: NullabilityQualifier?, mutability: MutabilityQualifier?): JavaTypeQualifiers {
|
||||
fun createJavaTypeQualifiers(
|
||||
nullability: NullabilityQualifier?,
|
||||
mutability: MutabilityQualifier?,
|
||||
forWarning: Boolean
|
||||
): JavaTypeQualifiers {
|
||||
if (!isAnyNonNullTypeParameter || nullability != NullabilityQualifier.NOT_NULL) {
|
||||
return JavaTypeQualifiers(nullability, mutability, false)
|
||||
return JavaTypeQualifiers(nullability, mutability, false, forWarning)
|
||||
}
|
||||
return JavaTypeQualifiers(
|
||||
nullability, mutability,
|
||||
isNotNullTypeParameter = true)
|
||||
return JavaTypeQualifiers(nullability, mutability, true, forWarning)
|
||||
}
|
||||
|
||||
if (isCovariant) {
|
||||
fun <T : Any> Set<T>.selectCovariantly(low: T, high: T, own: T?): T? {
|
||||
fun <T : Any> Set<T>.select(low: T, high: T, own: T?): T? {
|
||||
if (isCovariant) {
|
||||
val supertypeQualifier = if (low in this) low else if (high in this) high else null
|
||||
return if (supertypeQualifier == low && own == high) null else own ?: supertypeQualifier
|
||||
}
|
||||
return createJavaTypeQualifiers(
|
||||
nullabilityFromSupertypes.selectCovariantly(
|
||||
NullabilityQualifier.NOT_NULL, NullabilityQualifier.NULLABLE, own.nullability
|
||||
),
|
||||
mutabilityFromSupertypes.selectCovariantly(
|
||||
MutabilityQualifier.MUTABLE, MutabilityQualifier.READ_ONLY, own.mutability
|
||||
)
|
||||
)
|
||||
|
||||
// isInvariant
|
||||
val effectiveSet = own?.let { (this + own).toSet() } ?: this
|
||||
// if this set contains exactly one element, it is the qualifier everybody agrees upon,
|
||||
// otherwise (no qualifiers, or multiple qualifiers), there's no single such qualifier
|
||||
// and all qualifiers are discarded
|
||||
return effectiveSet.singleOrNull()
|
||||
}
|
||||
else {
|
||||
fun <T : Any> Set<T>.selectInvariantly(own: T?): T? {
|
||||
val effectiveSet = own?.let { (this + own).toSet() } ?: this
|
||||
// if this set contains exactly one element, it is the qualifier everybody agrees upon,
|
||||
// otherwise (no qualifiers, or multiple qualifiers), there's no single such qualifier
|
||||
// and all qualifiers are discarded
|
||||
return effectiveSet.singleOrNull()
|
||||
}
|
||||
return createJavaTypeQualifiers(
|
||||
nullabilityFromSupertypes.selectInvariantly(own.nullability),
|
||||
mutabilityFromSupertypes.selectInvariantly(own.mutability)
|
||||
)
|
||||
|
||||
val ownNullability = own.takeIf { !it.isNullabilityQualifierForWarning }?.nullability
|
||||
val ownNullabilityForWarning = own.nullability
|
||||
|
||||
val nullability = nullabilityFromSupertypes.select(NullabilityQualifier.NOT_NULL, NullabilityQualifier.NULLABLE, ownNullability)
|
||||
val mutability = mutabilityFromSupertypes.select(MutabilityQualifier.MUTABLE, MutabilityQualifier.READ_ONLY, own.mutability)
|
||||
|
||||
val canChange = ownNullabilityForWarning != ownNullability || nullabilityFromSupertypesWithWarning != null
|
||||
if (nullability == null && canChange) {
|
||||
val nullabilityWithWarning = (nullabilityFromSupertypesWithWarning ?: nullabilityFromSupertypes)
|
||||
.select(NullabilityQualifier.NOT_NULL, NullabilityQualifier.NULLABLE, ownNullabilityForWarning)
|
||||
|
||||
return createJavaTypeQualifiers(nullabilityWithWarning, mutability, true)
|
||||
}
|
||||
|
||||
return createJavaTypeQualifiers(nullability, mutability,nullability == null)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+17
-14
@@ -71,17 +71,15 @@ private fun UnwrappedType.enhancePossiblyFlexible(qualifiers: (Int) -> JavaTypeQ
|
||||
}
|
||||
|
||||
val wereChanges = lowerResult.wereChanges || upperResult.wereChanges
|
||||
val enhancement = lowerResult.type.getEnhancement() ?: upperResult.type.getEnhancement()
|
||||
val type = if (!wereChanges) this@enhancePossiblyFlexible
|
||||
else when {
|
||||
this is RawTypeImpl -> RawTypeImpl(lowerResult.type, upperResult.type)
|
||||
else -> KotlinTypeFactory.flexibleType(lowerResult.type, upperResult.type)
|
||||
}.wrapEnhancement(enhancement)
|
||||
|
||||
Result(
|
||||
if (wereChanges) {
|
||||
if (this is RawTypeImpl) {
|
||||
RawTypeImpl(lowerResult.type, upperResult.type)
|
||||
}
|
||||
else {
|
||||
KotlinTypeFactory.flexibleType(lowerResult.type, upperResult.type)
|
||||
}
|
||||
}
|
||||
else
|
||||
this@enhancePossiblyFlexible,
|
||||
type,
|
||||
lowerResult.subtreeSize,
|
||||
wereChanges
|
||||
)
|
||||
@@ -137,8 +135,11 @@ private fun SimpleType.enhanceInflexible(qualifiers: (Int) -> JavaTypeQualifiers
|
||||
enhancedNullability
|
||||
)
|
||||
|
||||
val result = if (effectiveQualifiers.isNotNullTypeParameter) NotNullTypeParameter(enhancedType) else enhancedType
|
||||
return SimpleResult(result, subtreeSize, wereChanges = true)
|
||||
val enhancement = if (effectiveQualifiers.isNotNullTypeParameter) NotNullTypeParameter(enhancedType) else enhancedType
|
||||
val nullabilityForWarning = enhancedNullabilityAnnotations != null && effectiveQualifiers.isNullabilityQualifierForWarning
|
||||
val result = if (nullabilityForWarning) wrapEnhancement(enhancement) else enhancement
|
||||
|
||||
return SimpleResult(result as SimpleType, subtreeSize, wereChanges = true)
|
||||
}
|
||||
|
||||
private fun List<Annotations>.compositeAnnotationsOrSingle() = when (size) {
|
||||
@@ -225,8 +226,10 @@ internal class NotNullTypeParameter(override val delegate: SimpleType) : CustomT
|
||||
|
||||
return when (unwrappedType) {
|
||||
is SimpleType -> unwrappedType.prepareReplacement()
|
||||
is FlexibleType -> KotlinTypeFactory.flexibleType(unwrappedType.lowerBound.prepareReplacement(),
|
||||
unwrappedType.upperBound.prepareReplacement())
|
||||
is FlexibleType -> KotlinTypeFactory.flexibleType(
|
||||
unwrappedType.lowerBound.prepareReplacement(),
|
||||
unwrappedType.upperBound.prepareReplacement()
|
||||
).wrapEnhancement(unwrappedType.getEnhancement())
|
||||
else -> error("Incorrect type: $unwrappedType")
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -29,7 +29,8 @@ enum class MutabilityQualifier {
|
||||
class JavaTypeQualifiers internal constructor(
|
||||
val nullability: NullabilityQualifier?,
|
||||
val mutability: MutabilityQualifier?,
|
||||
internal val isNotNullTypeParameter: Boolean
|
||||
internal val isNotNullTypeParameter: Boolean,
|
||||
internal val isNullabilityQualifierForWarning: Boolean = false
|
||||
) {
|
||||
companion object {
|
||||
val NONE = JavaTypeQualifiers(null, null, false)
|
||||
|
||||
+2
-1
@@ -47,7 +47,8 @@ internal class UnsafeVarianceTypeSubstitution(builtIns: KotlinBuiltIns) : TypeSu
|
||||
is FlexibleType ->
|
||||
KotlinTypeFactory.flexibleType(
|
||||
lowerBound.annotatePartsWithUnsafeVariance(subPathsWithIndex(unsafeVariancePaths, 0)),
|
||||
upperBound.annotatePartsWithUnsafeVariance(subPathsWithIndex(unsafeVariancePaths, 1)))
|
||||
upperBound.annotatePartsWithUnsafeVariance(subPathsWithIndex(unsafeVariancePaths, 1))
|
||||
).inheritEnhancement(this)
|
||||
is SimpleType -> annotatePartsWithUnsafeVariance(unsafeVariancePaths)
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -39,6 +39,7 @@ import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver
|
||||
import org.jetbrains.kotlin.serialization.deserialization.DeserializationComponents
|
||||
import org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration
|
||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||
import org.jetbrains.kotlin.utils.Jsr305State
|
||||
|
||||
class RuntimeModuleData private constructor(
|
||||
val deserialization: DeserializationComponents,
|
||||
@@ -58,7 +59,7 @@ class RuntimeModuleData private constructor(
|
||||
val runtimePackagePartProvider = RuntimePackagePartProvider(classLoader)
|
||||
val javaResolverCache = JavaResolverCache.EMPTY
|
||||
val notFoundClasses = NotFoundClasses(storageManager, module)
|
||||
val annotationTypeQualifierResolver = AnnotationTypeQualifierResolver.Empty
|
||||
val annotationTypeQualifierResolver = AnnotationTypeQualifierResolver(storageManager, Jsr305State.IGNORE)
|
||||
val globalJavaResolverContext = JavaResolverComponents(
|
||||
storageManager, ReflectJavaClassFinder(classLoader), reflectKotlinClassFinder, deserializedDescriptorResolver,
|
||||
ExternalAnnotationResolver.EMPTY, SignaturePropagator.DO_NOTHING, RuntimeErrorReporter, javaResolverCache,
|
||||
|
||||
@@ -415,6 +415,8 @@ fun ClassDescriptor.isSubclassOf(superclass: ClassDescriptor): Boolean = Descrip
|
||||
val AnnotationDescriptor.annotationClass: ClassDescriptor?
|
||||
get() = type.constructor.declarationDescriptor as? ClassDescriptor
|
||||
|
||||
fun AnnotationDescriptor.firstArgumentValue() = allValueArguments.values.firstOrNull()?.value
|
||||
|
||||
fun MemberDescriptor.isEffectivelyExternal(): Boolean {
|
||||
if (isExternal) return true
|
||||
|
||||
|
||||
@@ -112,8 +112,14 @@ fun approximateCapturedTypes(type: KotlinType): ApproximationBounds<KotlinType>
|
||||
val boundsForFlexibleUpper = approximateCapturedTypes(type.upperIfFlexible())
|
||||
|
||||
return ApproximationBounds(
|
||||
KotlinTypeFactory.flexibleType(boundsForFlexibleLower.lower.lowerIfFlexible(), boundsForFlexibleUpper.lower.upperIfFlexible()),
|
||||
KotlinTypeFactory.flexibleType(boundsForFlexibleLower.upper.lowerIfFlexible(), boundsForFlexibleUpper.upper.upperIfFlexible()))
|
||||
KotlinTypeFactory.flexibleType(
|
||||
boundsForFlexibleLower.lower.lowerIfFlexible(),
|
||||
boundsForFlexibleUpper.lower.upperIfFlexible()
|
||||
).inheritEnhancement(type),
|
||||
KotlinTypeFactory.flexibleType(
|
||||
boundsForFlexibleLower.upper.lowerIfFlexible(),
|
||||
boundsForFlexibleUpper.upper.upperIfFlexible()
|
||||
).inheritEnhancement(type))
|
||||
}
|
||||
|
||||
val typeConstructor = type.constructor
|
||||
|
||||
@@ -134,6 +134,21 @@ public class TypeSubstitutor {
|
||||
|
||||
// The type is within the substitution range, i.e. T or T?
|
||||
KotlinType type = originalProjection.getType();
|
||||
if (type instanceof TypeWithEnhancement) {
|
||||
KotlinType origin = ((TypeWithEnhancement) type).getOrigin();
|
||||
KotlinType enhancement = ((TypeWithEnhancement) type).getEnhancement();
|
||||
|
||||
TypeProjection substitution = unsafeSubstitute(
|
||||
new TypeProjectionImpl(originalProjection.getProjectionKind(), origin),
|
||||
recursionDepth + 1
|
||||
);
|
||||
|
||||
KotlinType substitutedEnhancement = substitute(enhancement, originalProjection.getProjectionKind());
|
||||
KotlinType resultingType = TypeWithEnhancementKt.wrapEnhancement(substitution.getType().unwrap(), substitutedEnhancement);
|
||||
|
||||
return new TypeProjectionImpl(substitution.getProjectionKind(), resultingType);
|
||||
}
|
||||
|
||||
if (DynamicTypesKt.isDynamic(type) || type.unwrap() instanceof RawType) {
|
||||
return originalProjection; // todo investigate
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ fun KotlinType.replaceArgumentsWithStarProjections(): KotlinType {
|
||||
unwrapped.upperBound.replaceArgumentsWithStarProjections()
|
||||
)
|
||||
is SimpleType -> unwrapped.replaceArgumentsWithStarProjections()
|
||||
}
|
||||
}.inheritEnhancement(unwrapped)
|
||||
}
|
||||
|
||||
private fun SimpleType.replaceArgumentsWithStarProjections(): SimpleType {
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2010-2017 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.types
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRendererOptions
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
|
||||
interface TypeWithEnhancement {
|
||||
val origin: UnwrappedType
|
||||
val enhancement: KotlinType
|
||||
}
|
||||
|
||||
class SimpleTypeWithEnhancement(
|
||||
override val delegate: SimpleType,
|
||||
override val enhancement: KotlinType
|
||||
) : DelegatingSimpleType(),
|
||||
TypeWithEnhancement {
|
||||
|
||||
override val origin: UnwrappedType get() = delegate
|
||||
|
||||
override fun replaceAnnotations(newAnnotations: Annotations): SimpleType
|
||||
= origin.replaceAnnotations(newAnnotations).wrapEnhancement(enhancement) as SimpleType
|
||||
|
||||
override fun makeNullableAsSpecified(newNullability: Boolean): SimpleType
|
||||
= origin.makeNullableAsSpecified(newNullability).wrapEnhancement(enhancement) as SimpleType
|
||||
}
|
||||
|
||||
class FlexibleTypeWithEnhancement(
|
||||
override val origin: FlexibleType,
|
||||
override val enhancement: KotlinType
|
||||
) : FlexibleType(origin.lowerBound, origin.upperBound),
|
||||
TypeWithEnhancement {
|
||||
|
||||
override fun replaceAnnotations(newAnnotations: Annotations): UnwrappedType
|
||||
= origin.replaceAnnotations(newAnnotations).wrapEnhancement(enhancement)
|
||||
|
||||
override fun makeNullableAsSpecified(newNullability: Boolean): UnwrappedType
|
||||
= origin.makeNullableAsSpecified(newNullability).wrapEnhancement(enhancement)
|
||||
|
||||
override fun render(renderer: DescriptorRenderer, options: DescriptorRendererOptions): String
|
||||
= origin.render(renderer, options)
|
||||
|
||||
override val delegate: SimpleType get() = origin.delegate
|
||||
}
|
||||
|
||||
fun KotlinType.getEnhancement(): KotlinType? = when (this) {
|
||||
is TypeWithEnhancement -> enhancement
|
||||
else -> null
|
||||
}
|
||||
|
||||
fun KotlinType.unwrapEnhancement(): KotlinType = getEnhancement() ?: this
|
||||
|
||||
fun UnwrappedType.inheritEnhancement(origin: KotlinType): UnwrappedType = wrapEnhancement(origin.getEnhancement())
|
||||
|
||||
fun UnwrappedType.wrapEnhancement(enhancement: KotlinType?): UnwrappedType {
|
||||
if (enhancement == null) {
|
||||
return this
|
||||
}
|
||||
|
||||
return when (this) {
|
||||
is SimpleType -> SimpleTypeWithEnhancement(this, enhancement)
|
||||
is FlexibleType -> FlexibleTypeWithEnhancement(this, enhancement)
|
||||
}
|
||||
}
|
||||
@@ -149,7 +149,7 @@ object NewKotlinTypeChecker : KotlinTypeChecker {
|
||||
type
|
||||
}
|
||||
}
|
||||
}
|
||||
}.inheritEnhancement(type)
|
||||
|
||||
private fun TypeCheckerContext.checkSubtypeForSpecialCases(subType: SimpleType, superType: SimpleType): Boolean? {
|
||||
if (subType.isError || superType.isError) {
|
||||
|
||||
@@ -119,7 +119,7 @@ class FlexibleTypeImpl(lowerBound: SimpleType, upperBound: SimpleType) : Flexibl
|
||||
return when(unwrapped) {
|
||||
is FlexibleType -> unwrapped
|
||||
is SimpleType -> KotlinTypeFactory.flexibleType(unwrapped, unwrapped.makeNullableAsSpecified(true))
|
||||
}
|
||||
}.inheritEnhancement(unwrapped)
|
||||
}
|
||||
|
||||
override fun replaceAnnotations(newAnnotations: Annotations): UnwrappedType
|
||||
|
||||
+2
-2
@@ -43,9 +43,9 @@ object IDELanguageSettingsProvider : LanguageSettingsProvider {
|
||||
val settings = KotlinFacetSettingsProvider.getInstance(project).getSettings(module) ?: continue
|
||||
val compilerArguments = settings.compilerArguments as? K2JVMCompilerArguments ?: continue
|
||||
|
||||
val jsr305state = Jsr305State.findByDescription(compilerArguments.jsr305GlobalReportLevel)
|
||||
val jsr305state = Jsr305State.findByDescription(compilerArguments.jsr305GlobalState)
|
||||
if (jsr305state != null && jsr305state != Jsr305State.IGNORE) {
|
||||
map.put(AnalysisFlag.loadJsr305Annotations, jsr305state)
|
||||
map.put(AnalysisFlag.jsr305GlobalState, jsr305state)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ class Jsr305HighlightingTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||
super.configureModule(module, model)
|
||||
module.createFacet(TargetPlatformKind.Jvm(JvmTarget.JVM_1_8))
|
||||
val facetSettings = KotlinFacetSettingsProvider.getInstance(project).getInitializedSettings(module)
|
||||
(facetSettings.compilerArguments as K2JVMCompilerArguments).jsr305GlobalReportLevel = Jsr305State.ENABLE.description
|
||||
(facetSettings.compilerArguments as K2JVMCompilerArguments).jsr305GlobalState = Jsr305State.ENABLE.description
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user