Sanitize declaration return types

On JVM, strip @EnhancedNullability annotation from inferred types for
functions, properties, and local variables, so that these annotations
do not "escape" from Kotlin declarations.
This commit is contained in:
Dmitry Petrov
2017-10-24 13:08:29 +03:00
parent 2b924f44d8
commit 57602c420d
13 changed files with 128 additions and 24 deletions
@@ -0,0 +1,43 @@
/*
* 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.resolve.jvm
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.annotations.FilteredAnnotations
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.DeclarationReturnTypeSanitizer
import org.jetbrains.kotlin.types.UnwrappedType
import org.jetbrains.kotlin.types.WrappedTypeFactory
object JvmDeclarationReturnTypeSanitizer : DeclarationReturnTypeSanitizer {
override fun sanitizeReturnType(
inferred: UnwrappedType,
wrappedTypeFactory: WrappedTypeFactory,
trace: BindingTrace,
languageVersionSettings: LanguageVersionSettings
): UnwrappedType =
if (languageVersionSettings.supportsFeature(LanguageFeature.StrictJavaNullabilityAssertions)) {
// NB can't check for presence of EnhancedNullability here,
// because it will also cause recursion in declaration type resolution.
inferred.replaceAnnotations(FilteredAnnotations(inferred.annotations) {
it != JvmAnnotationNames.ENHANCED_NULLABILITY_ANNOTATION
})
}
else inferred
}
@@ -81,7 +81,9 @@ object JvmPlatformConfigurator : PlatformConfigurator(
delegationFilter = JvmDelegationFilter,
overridesBackwardCompatibilityHelper = JvmOverridesBackwardCompatibilityHelper
overridesBackwardCompatibilityHelper = JvmOverridesBackwardCompatibilityHelper,
declarationReturnTypeSanitizer = JvmDeclarationReturnTypeSanitizer
) {
override fun configureModuleComponents(container: StorageComponentContainer) {
container.useImpl<JvmReflectionAPICallChecker>()
@@ -0,0 +1,40 @@
/*
* 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.resolve
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.UnwrappedType
import org.jetbrains.kotlin.types.WrappedTypeFactory
interface DeclarationReturnTypeSanitizer {
fun sanitizeReturnType(
inferred: UnwrappedType,
wrappedTypeFactory: WrappedTypeFactory,
trace: BindingTrace,
languageVersionSettings: LanguageVersionSettings
): UnwrappedType
object Default : DeclarationReturnTypeSanitizer {
override fun sanitizeReturnType(
inferred: UnwrappedType,
wrappedTypeFactory: WrappedTypeFactory,
trace: BindingTrace,
languageVersionSettings: LanguageVersionSettings
) = inferred
}
}
@@ -87,6 +87,7 @@ public class DescriptorResolver {
private final WrappedTypeFactory wrappedTypeFactory;
private final SyntheticResolveExtension syntheticResolveExtension;
private final TypeApproximator typeApproximator;
private final DeclarationReturnTypeSanitizer declarationReturnTypeSanitizer;
public DescriptorResolver(
@NotNull AnnotationResolver annotationResolver,
@@ -103,7 +104,8 @@ public class DescriptorResolver {
@NotNull ModifiersChecker modifiersChecker,
@NotNull WrappedTypeFactory wrappedTypeFactory,
@NotNull Project project,
TypeApproximator approximator
@NotNull TypeApproximator approximator,
@NotNull DeclarationReturnTypeSanitizer declarationReturnTypeSanitizer
) {
this.annotationResolver = annotationResolver;
this.builtIns = builtIns;
@@ -120,6 +122,7 @@ public class DescriptorResolver {
this.wrappedTypeFactory = wrappedTypeFactory;
this.syntheticResolveExtension = SyntheticResolveExtension.Companion.getInstance(project);
typeApproximator = approximator;
this.declarationReturnTypeSanitizer = declarationReturnTypeSanitizer;
}
public List<KotlinType> resolveSupertypes(
@@ -1152,12 +1155,12 @@ public class DescriptorResolver {
) {
return wrappedTypeFactory.createRecursionIntolerantDeferredType(trace, () -> {
PreliminaryDeclarationVisitor.Companion.createForDeclaration(function, trace, languageVersionSettings);
KotlinType type = expressionTypingServices.getBodyExpressionType(
trace, scope, dataFlowInfo, function, functionDescriptor);
KotlinType result = transformAnonymousTypeIfNeeded(functionDescriptor, function, type, trace);
UnwrappedType approximatedType = typeApproximator.approximateDeclarationType(result, false);
functionsTypingVisitor.checkTypesForReturnStatements(function, trace, approximatedType);
return approximatedType;
KotlinType type = expressionTypingServices.getBodyExpressionType(trace, scope, dataFlowInfo, function, functionDescriptor);
KotlinType publicType = transformAnonymousTypeIfNeeded(functionDescriptor, function, type, trace);
UnwrappedType approximatedType = typeApproximator.approximateDeclarationType(publicType, false);
KotlinType sanitizedType = declarationReturnTypeSanitizer.sanitizeReturnType(approximatedType, wrappedTypeFactory, trace, languageVersionSettings);
functionsTypingVisitor.checkTypesForReturnStatements(function, trace, sanitizedType);
return sanitizedType;
});
}
@@ -134,7 +134,7 @@ class FunctionDescriptorResolver(
assert(function.typeReference == null) {
"Return type must be initialized early for function: " + function.text + ", at: " + DiagnosticUtils.atLocation(function) }
val returnType = when {
val inferredReturnType = when {
function.hasBlockBody() ->
builtIns.unitType
function.hasBody() ->
@@ -142,7 +142,7 @@ class FunctionDescriptorResolver(
else ->
ErrorUtils.createErrorType("No type, no body")
}
functionDescriptor.setReturnType(returnType)
functionDescriptor.setReturnType(inferredReturnType)
}
fun initializeFunctionDescriptorAndExplicitReturnType(
@@ -24,7 +24,7 @@ interface IdentifierChecker {
fun checkIdentifier(identifier: PsiElement?, diagnosticHolder: DiagnosticSink)
fun checkDeclaration(declaration: KtDeclaration, diagnosticHolder: DiagnosticSink)
object DEFAULT : IdentifierChecker {
object Default : IdentifierChecker {
override fun checkIdentifier(identifier: PsiElement?, diagnosticHolder: DiagnosticSink) {}
override fun checkDeclaration(declaration: KtDeclaration, diagnosticHolder: DiagnosticSink) {}
}
@@ -20,11 +20,16 @@ import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.*
import org.jetbrains.kotlin.descriptors.impl.FunctionDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.VariableDescriptorWithInitializerImpl
import org.jetbrains.kotlin.diagnostics.Errors.*
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.KtPsiUtil
import org.jetbrains.kotlin.psi.KtVariableDeclaration
import org.jetbrains.kotlin.resolve.calls.context.ContextDependency
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptorNonRoot
interface OverloadFilter {
fun filterPackageMemberOverloads(overloads: Collection<DeclarationDescriptorNonRoot>): Collection<DeclarationDescriptorNonRoot>
object DEFAULT : OverloadFilter {
object Default : OverloadFilter {
override fun filterPackageMemberOverloads(overloads: Collection<DeclarationDescriptorNonRoot>): Collection<DeclarationDescriptorNonRoot> =
overloads
}
@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
interface OverridesBackwardCompatibilityHelper {
fun overrideCanBeOmitted(overridingDescriptor: CallableMemberDescriptor): Boolean
object DEFAULT : OverridesBackwardCompatibilityHelper {
object Default : OverridesBackwardCompatibilityHelper {
override fun overrideCanBeOmitted(overridingDescriptor: CallableMemberDescriptor): Boolean =
false
}
@@ -64,8 +64,9 @@ abstract class TargetPlatform(val platformName: String) {
override val platformConfigurator =
object : PlatformConfigurator(
DynamicTypesSettings(), listOf(), listOf(), listOf(), listOf(), listOf(),
IdentifierChecker.DEFAULT, OverloadFilter.DEFAULT, PlatformToKotlinClassMap.EMPTY, DelegationFilter.DEFAULT,
OverridesBackwardCompatibilityHelper.DEFAULT
IdentifierChecker.Default, OverloadFilter.Default, PlatformToKotlinClassMap.EMPTY, DelegationFilter.Default,
OverridesBackwardCompatibilityHelper.Default,
DeclarationReturnTypeSanitizer.Default
) {
override fun configureModuleComponents(container: StorageComponentContainer) {
container.useInstance(SyntheticScopes.Empty)
@@ -118,7 +119,8 @@ abstract class PlatformConfigurator(
private val overloadFilter: OverloadFilter,
private val platformToKotlinClassMap: PlatformToKotlinClassMap,
private val delegationFilter: DelegationFilter,
private val overridesBackwardCompatibilityHelper: OverridesBackwardCompatibilityHelper
private val overridesBackwardCompatibilityHelper: OverridesBackwardCompatibilityHelper,
private val declarationReturnTypeSanitizer: DeclarationReturnTypeSanitizer
) {
private val declarationCheckers: List<DeclarationChecker> = DEFAULT_DECLARATION_CHECKERS + additionalDeclarationCheckers
private val callCheckers: List<CallChecker> = DEFAULT_CALL_CHECKERS + additionalCallCheckers
@@ -139,6 +141,7 @@ abstract class PlatformConfigurator(
useInstance(platformToKotlinClassMap)
useInstance(delegationFilter)
useInstance(overridesBackwardCompatibilityHelper)
useInstance(declarationReturnTypeSanitizer)
}
}
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.resolve
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors
import org.jetbrains.kotlin.descriptors.impl.VariableDescriptorWithInitializerImpl
import org.jetbrains.kotlin.diagnostics.Errors
@@ -39,7 +40,9 @@ class VariableTypeAndInitializerResolver(
private val constantExpressionEvaluator: ConstantExpressionEvaluator,
private val delegatedPropertyResolver: DelegatedPropertyResolver,
private val wrappedTypeFactory: WrappedTypeFactory,
private val typeApproximator: TypeApproximator
private val typeApproximator: TypeApproximator,
private val declarationReturnTypeSanitizer: DeclarationReturnTypeSanitizer,
private val languageVersionSettings: LanguageVersionSettings
) {
companion object {
@JvmField
@@ -92,6 +95,7 @@ class VariableTypeAndInitializerResolver(
else -> resolveInitializerType(scopeForInitializer, variable.initializer!!, dataFlowInfo, trace, local)
}
else -> null
}
}
@@ -152,7 +156,9 @@ class VariableTypeAndInitializerResolver(
trace: BindingTrace,
local: Boolean
): KotlinType {
return approximateType(expressionTypingServices.safeGetType(scope, initializer, TypeUtils.NO_EXPECTED_TYPE, dataFlowInfo, trace), local)
val inferredType = expressionTypingServices.safeGetType(scope, initializer, TypeUtils.NO_EXPECTED_TYPE, dataFlowInfo, trace)
val approximatedType = approximateType(inferredType, local)
return declarationReturnTypeSanitizer.sanitizeReturnType(approximatedType, wrappedTypeFactory, trace, languageVersionSettings)
}
private fun approximateType(type: KotlinType, local: Boolean): UnwrappedType = typeApproximator.approximateDeclarationType(type, local)
@@ -23,7 +23,7 @@ interface DelegationFilter {
fun filter(interfaceMember: CallableMemberDescriptor, languageVersionSettings: LanguageVersionSettings): Boolean
object DEFAULT : DelegationFilter {
object Default : DelegationFilter {
override fun filter(interfaceMember: CallableMemberDescriptor, languageVersionSettings: LanguageVersionSettings) = true
}
}
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.container.useInstance
import org.jetbrains.kotlin.js.naming.NameSuggestion
import org.jetbrains.kotlin.js.resolve.diagnostics.*
import org.jetbrains.kotlin.platform.PlatformToKotlinClassMap
import org.jetbrains.kotlin.resolve.DeclarationReturnTypeSanitizer
import org.jetbrains.kotlin.resolve.OverloadFilter
import org.jetbrains.kotlin.resolve.OverridesBackwardCompatibilityHelper
import org.jetbrains.kotlin.resolve.PlatformConfigurator
@@ -51,10 +52,11 @@ object JsPlatformConfigurator : PlatformConfigurator(
additionalClassifierUsageCheckers = listOf(),
additionalAnnotationCheckers = listOf(),
identifierChecker = JsIdentifierChecker,
overloadFilter = OverloadFilter.DEFAULT,
overloadFilter = OverloadFilter.Default,
platformToKotlinClassMap = PlatformToKotlinClassMap.EMPTY,
delegationFilter = DelegationFilter.DEFAULT,
overridesBackwardCompatibilityHelper = OverridesBackwardCompatibilityHelper.DEFAULT
delegationFilter = DelegationFilter.Default,
overridesBackwardCompatibilityHelper = OverridesBackwardCompatibilityHelper.Default,
declarationReturnTypeSanitizer = DeclarationReturnTypeSanitizer.Default
) {
override fun configureModuleComponents(container: StorageComponentContainer) {
container.useInstance(NameSuggestion())