Add warnings for jsr305 nullable annotations

#KT-19115 Fixed
This commit is contained in:
e5l
2017-08-11 14:21:23 +03:00
parent 746de612ad
commit 5501cdf049
33 changed files with 506 additions and 198 deletions
@@ -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)
@@ -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
}
}
@@ -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!!))
}
}
}
}
}
@@ -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,
@@ -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() {
{
@@ -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
}
@@ -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
View File
@@ -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
@@ -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());
}
@@ -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)
}
@@ -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
}
}
}
@@ -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(
@@ -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()
@@ -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)
}
}
@@ -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")
}
}
@@ -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)
@@ -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)
}
}
@@ -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
@@ -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
}
}
}