From 57602c420d711a7c03311520c5cadc103a232d57 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Tue, 24 Oct 2017 13:08:29 +0300 Subject: [PATCH] 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. --- .../jvm/JvmDeclarationReturnTypeSanitizer.kt | 43 +++++++++++++++++++ .../jvm/platform/JvmPlatformConfigurator.kt | 4 +- .../resolve/DeclarationReturnTypeSanitizer.kt | 40 +++++++++++++++++ .../kotlin/resolve/DescriptorResolver.java | 17 +++++--- .../resolve/FunctionDescriptorResolver.kt | 4 +- .../kotlin/resolve/IdentifierChecker.kt | 2 +- .../kotlin/resolve/LocalVariableResolver.kt | 9 +++- .../kotlin/resolve/OverloadFilter.kt | 2 +- .../OverridesBackwardCompatibilityHelper.kt | 2 +- .../kotlin/resolve/TargetPlatform.kt | 9 ++-- .../VariableTypeAndInitializerResolver.kt | 10 ++++- .../kotlin/resolve/lazy/DelegationFilter.kt | 2 +- .../js/resolve/JsPlatformConfigurator.kt | 8 ++-- 13 files changed, 128 insertions(+), 24 deletions(-) create mode 100644 compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/JvmDeclarationReturnTypeSanitizer.kt create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationReturnTypeSanitizer.kt diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/JvmDeclarationReturnTypeSanitizer.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/JvmDeclarationReturnTypeSanitizer.kt new file mode 100644 index 00000000000..c5a0dabc741 --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/JvmDeclarationReturnTypeSanitizer.kt @@ -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 +} \ No newline at end of file diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt index 8b22181c114..1e90097c2e0 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt @@ -81,7 +81,9 @@ object JvmPlatformConfigurator : PlatformConfigurator( delegationFilter = JvmDelegationFilter, - overridesBackwardCompatibilityHelper = JvmOverridesBackwardCompatibilityHelper + overridesBackwardCompatibilityHelper = JvmOverridesBackwardCompatibilityHelper, + + declarationReturnTypeSanitizer = JvmDeclarationReturnTypeSanitizer ) { override fun configureModuleComponents(container: StorageComponentContainer) { container.useImpl() diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationReturnTypeSanitizer.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationReturnTypeSanitizer.kt new file mode 100644 index 00000000000..4100dcbd4f8 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationReturnTypeSanitizer.kt @@ -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 + } +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java index 34a820a17e1..2c26cd6e7d3 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java @@ -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 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; }); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt index f66fc96c116..c165fdbdd42 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt @@ -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( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/IdentifierChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/IdentifierChecker.kt index 4bf350c0e61..a6e0318a26d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/IdentifierChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/IdentifierChecker.kt @@ -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) {} } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/LocalVariableResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/LocalVariableResolver.kt index faac4f366b9..a2ea5c46512 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/LocalVariableResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/LocalVariableResolver.kt @@ -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 diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverloadFilter.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverloadFilter.kt index a81a85ce44d..b02281c7f26 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverloadFilter.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverloadFilter.kt @@ -22,7 +22,7 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptorNonRoot interface OverloadFilter { fun filterPackageMemberOverloads(overloads: Collection): Collection - object DEFAULT : OverloadFilter { + object Default : OverloadFilter { override fun filterPackageMemberOverloads(overloads: Collection): Collection = overloads } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverridesBackwardCompatibilityHelper.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverridesBackwardCompatibilityHelper.kt index fbe3ad6498d..8204d2b5fec 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverridesBackwardCompatibilityHelper.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverridesBackwardCompatibilityHelper.kt @@ -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 } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt index f5f0af0f593..29462efbe02 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt @@ -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 = DEFAULT_DECLARATION_CHECKERS + additionalDeclarationCheckers private val callCheckers: List = DEFAULT_CALL_CHECKERS + additionalCallCheckers @@ -139,6 +141,7 @@ abstract class PlatformConfigurator( useInstance(platformToKotlinClassMap) useInstance(delegationFilter) useInstance(overridesBackwardCompatibilityHelper) + useInstance(declarationReturnTypeSanitizer) } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/VariableTypeAndInitializerResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/VariableTypeAndInitializerResolver.kt index 32bf5ff6334..095101ccc02 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/VariableTypeAndInitializerResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/VariableTypeAndInitializerResolver.kt @@ -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) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/DelegationFilter.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/DelegationFilter.kt index c81f48219a9..98b72318ae1 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/DelegationFilter.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/DelegationFilter.kt @@ -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 } } \ No newline at end of file diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/JsPlatformConfigurator.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/JsPlatformConfigurator.kt index 214374f716b..127d73b780a 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/JsPlatformConfigurator.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/JsPlatformConfigurator.kt @@ -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())