From 759cb83dfb131ceaebdd5ead325c9e361cdd9268 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Thu, 2 Jun 2016 19:23:11 +0300 Subject: [PATCH] Add checks that coroutines are available at the given language level --- .../kotlin/config/LanguageFeatureSettings.kt | 1 + .../jetbrains/kotlin/diagnostics/Errors.java | 2 + .../rendering/DefaultErrorMessages.java | 1 + .../kotlin/resolve/BodyResolver.java | 2 +- .../kotlin/resolve/DeclarationsChecker.kt | 11 +++-- .../kotlin/resolve/ModifiersChecker.java | 27 ++++++++--- .../kotlin/resolve/ModifiersChecker.kt | 46 +++++++++++++++++-- .../kotlin/resolve/OperatorModifierChecker.kt | 20 ++++++-- .../kotlin/resolve/TargetPlatform.kt | 1 - .../jetbrains/kotlin/resolve/TypeResolver.kt | 2 +- .../kotlin/resolve/calls/CallCompleter.kt | 6 ++- .../kotlin/resolve/calls/CallResolver.java | 8 +++- ...CallChecker.kt => coroutineCallChecker.kt} | 18 ++++++++ .../ControlStructureTypingVisitor.java | 2 +- .../ExpressionTypingComponents.java | 13 +++--- .../tests/coroutines/unsupported.kt | 24 ++++++++++ .../tests/coroutines/unsupported.txt | 13 ++++++ .../checkers/DiagnosticsTestGenerated.java | 6 +++ 18 files changed, 170 insertions(+), 33 deletions(-) rename compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/{CoroutineSuspendCallChecker.kt => coroutineCallChecker.kt} (69%) create mode 100644 compiler/testData/diagnostics/tests/coroutines/unsupported.kt create mode 100644 compiler/testData/diagnostics/tests/coroutines/unsupported.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/config/LanguageFeatureSettings.kt b/compiler/frontend/src/org/jetbrains/kotlin/config/LanguageFeatureSettings.kt index 2caa1910388..bcc4faf0716 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/config/LanguageFeatureSettings.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/config/LanguageFeatureSettings.kt @@ -24,6 +24,7 @@ enum class LanguageFeature(val sinceVersion: LanguageVersion) { BoundCallableReferences(KOTLIN_1_1), LocalDelegatedProperties(KOTLIN_1_1), TopLevelSealedInheritance(KOTLIN_1_1), + Coroutines(KOTLIN_1_1) ; companion object { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index bc97d6a4265..ecbd2080908 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -22,6 +22,7 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.impl.source.tree.LeafPsiElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.cfg.WhenMissingCase; +import org.jetbrains.kotlin.config.LanguageFeature; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.lexer.KtKeywordToken; import org.jetbrains.kotlin.lexer.KtModifierKeywordToken; @@ -54,6 +55,7 @@ public interface Errors { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// DiagnosticFactory1 UNSUPPORTED = DiagnosticFactory1.create(ERROR); + DiagnosticFactory1 UNSUPPORTED_FEATURE = DiagnosticFactory1.create(ERROR); DiagnosticFactory1 EXCEPTION_FROM_ANALYZER = DiagnosticFactory1.create(ERROR); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 079915321b7..0e3df96205c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -497,6 +497,7 @@ public class DefaultErrorMessages { MAP.put(UNSAFE_IMPLICIT_INVOKE_CALL, "Reference has a nullable type ''{0}'', use explicit ''?.invoke()'' to make a function-like call instead", RENDER_TYPE); MAP.put(AMBIGUOUS_LABEL, "Ambiguous label"); MAP.put(UNSUPPORTED, "Unsupported [{0}]", STRING); + MAP.put(UNSUPPORTED_FEATURE, "{0} are unsupported at this language level", TO_STRING); MAP.put(EXCEPTION_FROM_ANALYZER, "Internal Error occurred while analyzing this expression:\n{0}", THROWABLE); MAP.put(UNNECESSARY_SAFE_CALL, "Unnecessary safe call on a non-null receiver of type {0}", RENDER_TYPE); MAP.put(UNEXPECTED_SAFE_CALL, "Safe-call is not allowed here"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java index 8ed3f38d938..e13d08320bd 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java @@ -558,7 +558,7 @@ public class BodyResolver { private void processModifiersOnInitializer(@NotNull KtModifierListOwner owner, @NotNull LexicalScope scope) { annotationChecker.check(owner, trace, null); - ModifierCheckerCore.INSTANCE.check(owner, trace, null); + ModifierCheckerCore.INSTANCE.check(owner, trace, null, languageFeatureSettings); KtModifierList modifierList = owner.getModifierList(); if (modifierList == null) return; diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt index cb022a0a6d4..4ddcd7971ff 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt @@ -20,6 +20,7 @@ import com.google.common.collect.ImmutableSet import com.google.common.collect.Sets import com.intellij.psi.PsiElement import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.config.LanguageFeatureSettings import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0 import org.jetbrains.kotlin.diagnostics.Errors @@ -70,10 +71,11 @@ internal class DeclarationsCheckerBuilder( private val descriptorResolver: DescriptorResolver, private val originalModifiersChecker: ModifiersChecker, private val annotationChecker: AnnotationChecker, - private val identifierChecker: IdentifierChecker + private val identifierChecker: IdentifierChecker, + private val languageFeatureSettings: LanguageFeatureSettings ) { fun withTrace(trace: BindingTrace) = - DeclarationsChecker(descriptorResolver, originalModifiersChecker, annotationChecker, identifierChecker, trace) + DeclarationsChecker(descriptorResolver, originalModifiersChecker, annotationChecker, identifierChecker, trace, languageFeatureSettings) } class DeclarationsChecker( @@ -81,7 +83,8 @@ class DeclarationsChecker( modifiersChecker: ModifiersChecker, private val annotationChecker: AnnotationChecker, private val identifierChecker: IdentifierChecker, - private val trace: BindingTrace + private val trace: BindingTrace, + private val languageFeatureSettings: LanguageFeatureSettings ) { private val modifiersChecker = modifiersChecker.withTrace(trace) @@ -190,7 +193,7 @@ class DeclarationsChecker( } } annotationChecker.check(packageDirective, trace, null) - ModifierCheckerCore.check(packageDirective, trace, null) + ModifierCheckerCore.check(packageDirective, trace, descriptor = null, languageFeatureSettings = languageFeatureSettings) } private fun checkTypesInClassHeader(classOrObject: KtClassOrObject) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java index c430fa1d43c..e59e49f85a5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java @@ -21,6 +21,7 @@ import com.google.common.collect.Sets; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.kotlin.config.LanguageFeatureSettings; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1; import org.jetbrains.kotlin.lexer.KtKeywordToken; @@ -157,9 +158,12 @@ public class ModifiersChecker { @NotNull private final BindingTrace trace; + @NotNull + private final LanguageFeatureSettings languageFeatureSettings; - private ModifiersCheckingProcedure(@NotNull BindingTrace trace) { + private ModifiersCheckingProcedure(@NotNull BindingTrace trace, LanguageFeatureSettings languageFeatureSettings) { this.trace = trace; + this.languageFeatureSettings = languageFeatureSettings; } public void checkParameterHasNoValOrVar( @@ -182,7 +186,7 @@ public class ModifiersChecker { AnnotationUseSiteTargetChecker.INSTANCE.check(modifierListOwner, descriptor, trace); runDeclarationCheckers(modifierListOwner, descriptor); annotationChecker.check(modifierListOwner, trace, descriptor); - ModifierCheckerCore.INSTANCE.check(modifierListOwner, trace, descriptor); + ModifierCheckerCore.INSTANCE.check(modifierListOwner, trace, descriptor, languageFeatureSettings); } public void checkModifiersForLocalDeclaration( @@ -194,10 +198,10 @@ public class ModifiersChecker { public void checkModifiersForDestructuringDeclaration(@NotNull KtDestructuringDeclaration multiDeclaration) { annotationChecker.check(multiDeclaration, trace, null); - ModifierCheckerCore.INSTANCE.check(multiDeclaration, trace, null); + ModifierCheckerCore.INSTANCE.check(multiDeclaration, trace, null, languageFeatureSettings); for (KtDestructuringDeclarationEntry multiEntry: multiDeclaration.getEntries()) { annotationChecker.check(multiEntry, trace, null); - ModifierCheckerCore.INSTANCE.check(multiEntry, trace, null); + ModifierCheckerCore.INSTANCE.check(multiEntry, trace, null, languageFeatureSettings); UnderscoreChecker.INSTANCE.checkNamed(multiEntry, trace); } } @@ -235,13 +239,14 @@ public class ModifiersChecker { for (DeclarationChecker checker : declarationCheckers) { checker.check(declaration, descriptor, trace, trace.getBindingContext()); } + OperatorModifierChecker.INSTANCE.check(declaration, descriptor, trace, languageFeatureSettings); } public void checkTypeParametersModifiers(@NotNull KtModifierListOwner modifierListOwner) { if (!(modifierListOwner instanceof KtTypeParameterListOwner)) return; List typeParameters = ((KtTypeParameterListOwner) modifierListOwner).getTypeParameters(); for (KtTypeParameter typeParameter : typeParameters) { - ModifierCheckerCore.INSTANCE.check(typeParameter, trace, null); + ModifierCheckerCore.INSTANCE.check(typeParameter, trace, null, languageFeatureSettings); } } } @@ -252,13 +257,21 @@ public class ModifiersChecker { @NotNull private final Iterable declarationCheckers; - public ModifiersChecker(@NotNull AnnotationChecker annotationChecker, @NotNull Iterable declarationCheckers) { + @NotNull + private final LanguageFeatureSettings languageFeatureSettings; + + public ModifiersChecker( + @NotNull AnnotationChecker annotationChecker, + @NotNull Iterable declarationCheckers, + @NotNull LanguageFeatureSettings languageFeatureSettings + ) { this.annotationChecker = annotationChecker; this.declarationCheckers = declarationCheckers; + this.languageFeatureSettings = languageFeatureSettings; } @NotNull public ModifiersCheckingProcedure withTrace(@NotNull BindingTrace trace) { - return new ModifiersCheckingProcedure(trace); + return new ModifiersCheckingProcedure(trace, languageFeatureSettings); } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.kt index 2ed1301dc32..d52afb0d493 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.kt @@ -19,6 +19,8 @@ package org.jetbrains.kotlin.resolve import com.intellij.lang.ASTNode import com.intellij.psi.PsiElement import com.intellij.psi.tree.TokenSet +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.config.LanguageFeatureSettings import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor @@ -90,6 +92,11 @@ object ModifierCheckerCore { INFIX_KEYWORD to EnumSet.of(FUNCTION) ) + val featureDependencies = mapOf( + COROUTINE_KEYWORD to LanguageFeature.Coroutines, + SUSPEND_KEYWORD to LanguageFeature.Coroutines + ) + // NOTE: deprecated targets must be possible! private val deprecatedTargetMap = mapOf>() @@ -242,6 +249,23 @@ object ModifierCheckerCore { return true } + private fun checkLanguageLevelSupport( + trace: BindingTrace, + node: ASTNode, + languageFeatureSettings: LanguageFeatureSettings + ): Boolean { + val modifier = node.elementType as KtModifierKeywordToken + val dependency = featureDependencies[modifier] ?: return true + + if (!languageFeatureSettings.supportsFeature(dependency)) { + trace.report(Errors.UNSUPPORTED_FEATURE.on(node.psi, dependency)) + return false + } + + return true + } + + // Should return false if error is reported, true otherwise private fun checkParent(trace: BindingTrace, node: ASTNode, parentDescriptor: DeclarationDescriptor?): Boolean { val modifier = node.elementType as KtModifierKeywordToken @@ -264,7 +288,13 @@ object ModifierCheckerCore { private val MODIFIER_KEYWORD_SET = TokenSet.orSet(KtTokens.SOFT_KEYWORDS, TokenSet.create(KtTokens.IN_KEYWORD)) - private fun checkModifierList(list: KtModifierList, trace: BindingTrace, parentDescriptor: DeclarationDescriptor?, actualTargets: List) { + private fun checkModifierList( + list: KtModifierList, + trace: BindingTrace, + parentDescriptor: DeclarationDescriptor?, + actualTargets: List, + languageFeatureSettings: LanguageFeatureSettings + ) { // It's a list of all nodes with error already reported // General strategy: report no more than one error but any number of warnings val incorrectNodes = hashSetOf() @@ -283,21 +313,29 @@ object ModifierCheckerCore { else if (!checkParent(trace, second, parentDescriptor)) { incorrectNodes += second } + else if (!checkLanguageLevelSupport(trace, second, languageFeatureSettings)) { + incorrectNodes += second + } } } } - fun check(listOwner: KtModifierListOwner, trace: BindingTrace, descriptor: DeclarationDescriptor?) { + fun check( + listOwner: KtModifierListOwner, + trace: BindingTrace, + descriptor: DeclarationDescriptor?, + languageFeatureSettings: LanguageFeatureSettings + ) { if (listOwner is KtDeclarationWithBody) { // JetFunction or JetPropertyAccessor for (parameter in listOwner.valueParameters) { if (!parameter.hasValOrVar()) { - check(parameter, trace, null) + check(parameter, trace, null, languageFeatureSettings) } } } val actualTargets = AnnotationChecker.getDeclarationSiteActualTargetList(listOwner, descriptor as? ClassDescriptor, trace) val list = listOwner.modifierList ?: return - checkModifierList(list, trace, descriptor?.containingDeclaration, actualTargets) + checkModifierList(list, trace, descriptor?.containingDeclaration, actualTargets, languageFeatureSettings) } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/OperatorModifierChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/OperatorModifierChecker.kt index 598489f08ca..27715890d1c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/OperatorModifierChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/OperatorModifierChecker.kt @@ -32,6 +32,8 @@ package org.jetbrains.kotlin.resolve +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.config.LanguageFeatureSettings import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.diagnostics.DiagnosticSink @@ -40,20 +42,28 @@ import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.util.CheckResult import org.jetbrains.kotlin.util.OperatorChecks +import org.jetbrains.kotlin.util.OperatorNameConventions -class OperatorModifierChecker : DeclarationChecker { - override fun check( +object OperatorModifierChecker { + fun check( declaration: KtDeclaration, descriptor: DeclarationDescriptor, diagnosticHolder: DiagnosticSink, - bindingContext: BindingContext + languageFeatureSettings: LanguageFeatureSettings ) { val functionDescriptor = descriptor as? FunctionDescriptor ?: return if (!functionDescriptor.isOperator) return val modifier = declaration.modifierList?.getModifier(KtTokens.OPERATOR_KEYWORD) ?: return val checkResult = OperatorChecks.checkOperator(functionDescriptor) - if (checkResult.isSuccess) return + if (checkResult.isSuccess) { + if (functionDescriptor.name == OperatorNameConventions.COROUTINE_HANDLE_RESULT + && !languageFeatureSettings.supportsFeature(LanguageFeature.Coroutines) + ) { + diagnosticHolder.report(Errors.UNSUPPORTED_FEATURE.on(modifier, LanguageFeature.Coroutines)) + } + return + } val errorDescription = if (checkResult is CheckResult.IllegalSignature) checkResult.error @@ -62,4 +72,4 @@ class OperatorModifierChecker : DeclarationChecker { diagnosticHolder.report(Errors.INAPPLICABLE_OPERATOR_MODIFIER.on(modifier, errorDescription)) } -} \ No newline at end of file +} diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt index 97438985760..0cec76a88d0 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt @@ -62,7 +62,6 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf( ConstModifierChecker, UnderscoreChecker, InlineParameterChecker, - OperatorModifierChecker(), InfixModifierChecker(), SuspendModifierChecker, CoroutineModifierChecker) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt index 39af9feccf8..5a38c558c82 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt @@ -673,7 +673,7 @@ class TypeResolver( ): List { return argumentElements.mapIndexed { i, argumentElement -> val projectionKind = argumentElement.projectionKind - ModifierCheckerCore.check(argumentElement, c.trace, null) + ModifierCheckerCore.check(argumentElement, c.trace, null, languageFeatureSettings) if (projectionKind == KtProjectionKind.STAR) { val parameters = constructor.parameters if (parameters.size > i) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallCompleter.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallCompleter.kt index 1776365b50a..419c5a7ff33 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallCompleter.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallCompleter.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.resolve.calls import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.config.LanguageFeatureSettings import org.jetbrains.kotlin.coroutines.controllerTypeIfCoroutine import org.jetbrains.kotlin.coroutines.resolveCoroutineHandleResultCallIfNeeded import org.jetbrains.kotlin.descriptors.CallableDescriptor @@ -27,6 +28,7 @@ import org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode. import org.jetbrains.kotlin.resolve.calls.callResolverUtil.getEffectiveExpectedType import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isInvokeCallOnVariable import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker +import org.jetbrains.kotlin.resolve.calls.checkers.checkCoroutineBuilderCall import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext import org.jetbrains.kotlin.resolve.calls.context.CallCandidateResolutionContext import org.jetbrains.kotlin.resolve.calls.context.CallPosition @@ -58,7 +60,8 @@ class CallCompleter( private val dataFlowAnalyzer: DataFlowAnalyzer, private val callCheckers: Iterable, private val builtIns: KotlinBuiltIns, - private val fakeCallResolver: FakeCallResolver + private val fakeCallResolver: FakeCallResolver, + private val languageFeatureSettings: LanguageFeatureSettings ) { fun completeCall( context: BasicCallResolutionContext, @@ -92,6 +95,7 @@ class CallCompleter( symbolUsageValidator.validateCall(resolvedCall, resolvedCall.resultingDescriptor, context.trace, element!!) resolveHandleResultCallForCoroutineLambdaExpressions(context, resolvedCall) + checkCoroutineBuilderCall(resolvedCall, context, languageFeatureSettings) } if (results.isSingleResult && results.resultingCall.status.isSuccess) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java index c04d808a18d..26e9a8b4ca4 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java @@ -22,6 +22,7 @@ import kotlin.jvm.functions.Function0; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.builtins.KotlinBuiltIns; +import org.jetbrains.kotlin.config.LanguageFeatureSettings; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.annotations.Annotations; import org.jetbrains.kotlin.name.Name; @@ -70,13 +71,16 @@ public class CallResolver { private CallCompleter callCompleter; private NewResolutionOldInference newCallResolver; private final KotlinBuiltIns builtIns; + private final LanguageFeatureSettings languageFeatureSettings; private static final PerformanceCounter callResolvePerfCounter = PerformanceCounter.Companion.create("Call resolve", ExpressionTypingVisitorDispatcher.typeInfoPerfCounter); public CallResolver( - @NotNull KotlinBuiltIns builtIns + @NotNull KotlinBuiltIns builtIns, + @NotNull LanguageFeatureSettings languageFeatureSettings ) { this.builtIns = builtIns; + this.languageFeatureSettings = languageFeatureSettings; } // component dependency cycle @@ -583,7 +587,7 @@ public class CallResolver { for (KtTypeProjection projection : typeArguments) { if (projection.getProjectionKind() != KtProjectionKind.NONE) { context.trace.report(PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT.on(projection)); - ModifierCheckerCore.INSTANCE.check(projection, context.trace, null); + ModifierCheckerCore.INSTANCE.check(projection, context.trace, null, languageFeatureSettings); } KotlinType type = argumentTypeResolver.resolveTypeRefWithDefault( projection.getTypeReference(), context.scope, context.trace, diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/CoroutineSuspendCallChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/coroutineCallChecker.kt similarity index 69% rename from compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/CoroutineSuspendCallChecker.kt rename to compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/coroutineCallChecker.kt index 330caa9b368..f0ed09b9e06 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/CoroutineSuspendCallChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/coroutineCallChecker.kt @@ -16,6 +16,8 @@ package org.jetbrains.kotlin.resolve.calls.checkers +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.config.LanguageFeatureSettings import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.psi.KtExpression @@ -37,3 +39,19 @@ object CoroutineSuspendCallChecker : CallChecker { } } } + +// It can't be another CallChecker implementation because of 3rd parameter +fun checkCoroutineBuilderCall( + resolvedCall: ResolvedCall<*>, + context: BasicCallResolutionContext, + languageFeatureSettings: LanguageFeatureSettings +) { + val descriptor = resolvedCall.candidateDescriptor as? FunctionDescriptor ?: return + if (descriptor.valueParameters.any { it.isCoroutine } + && !languageFeatureSettings.supportsFeature(LanguageFeature.Coroutines)) { + context.trace.report( + Errors.UNSUPPORTED_FEATURE.on( + resolvedCall.call.calleeExpression ?: resolvedCall.call.callElement, LanguageFeature.Coroutines)) + } +} + diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java index bcea4bd8770..09883aa2655 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java @@ -489,7 +489,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { components.identifierChecker.checkDeclaration(catchParameter, context.trace); ModifiersChecker.ModifiersCheckingProcedure modifiersChecking = components.modifiersChecker.withTrace(context.trace); modifiersChecking.checkParameterHasNoValOrVar(catchParameter, VAL_OR_VAR_ON_CATCH_PARAMETER); - ModifierCheckerCore.INSTANCE.check(catchParameter, context.trace, null); + ModifierCheckerCore.INSTANCE.check(catchParameter, context.trace, null, components.languageFeatureSettings); VariableDescriptor variableDescriptor = components.descriptorResolver.resolveLocalVariableDescriptor( context.scope, catchParameter, context.trace); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingComponents.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingComponents.java index 9d0063b0094..bdf65f4f91e 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingComponents.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingComponents.java @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.types.expressions; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.builtins.ReflectionTypes; +import org.jetbrains.kotlin.config.LanguageFeatureSettings; import org.jetbrains.kotlin.context.GlobalContext; import org.jetbrains.kotlin.incremental.components.LookupTracker; import org.jetbrains.kotlin.platform.PlatformToKotlinClassMap; @@ -61,8 +62,8 @@ public class ExpressionTypingComponents { /*package*/ DeclarationsCheckerBuilder declarationsCheckerBuilder; /*package*/ LocalVariableResolver localVariableResolver; /*package*/ LookupTracker lookupTracker; - /*package*/ DelegatedPropertyResolver delegatedPropertyResolver; /*package*/ OverloadChecker overloadChecker; + /*package*/ LanguageFeatureSettings languageFeatureSettings; @Inject public void setGlobalContext(@NotNull GlobalContext globalContext) { @@ -209,13 +210,13 @@ public class ExpressionTypingComponents { this.lookupTracker = lookupTracker; } - @Inject - public void setDelegatedPropertyResolver(DelegatedPropertyResolver delegatedPropertyResolver) { - this.delegatedPropertyResolver = delegatedPropertyResolver; - } - @Inject public void setOverloadChecker(OverloadChecker overloadChecker) { this.overloadChecker = overloadChecker; } + + @Inject + public void setLanguageFeatureSettings(@NotNull LanguageFeatureSettings languageFeatureSettings) { + this.languageFeatureSettings = languageFeatureSettings; + } } diff --git a/compiler/testData/diagnostics/tests/coroutines/unsupported.kt b/compiler/testData/diagnostics/tests/coroutines/unsupported.kt new file mode 100644 index 00000000000..139519b1f1c --- /dev/null +++ b/compiler/testData/diagnostics/tests/coroutines/unsupported.kt @@ -0,0 +1,24 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER +// !LANGUAGE: -Coroutines + +class Controller { + suspend fun suspendHere(x: Continuation) { + x.resume("OK") + } + + operator fun handleResult(x: String, y: Continuation) {} +} + +fun builder(coroutine c: Controller.() -> Continuation) { + c(Controller()).resume(Unit) +} + +fun box(): String { + var result = "" + + builder { + suspendHere() + } + + return result +} diff --git a/compiler/testData/diagnostics/tests/coroutines/unsupported.txt b/compiler/testData/diagnostics/tests/coroutines/unsupported.txt new file mode 100644 index 00000000000..c554e40fa6d --- /dev/null +++ b/compiler/testData/diagnostics/tests/coroutines/unsupported.txt @@ -0,0 +1,13 @@ +package + +public fun box(): kotlin.String +public fun builder(/*0*/ coroutine c: Controller.() -> kotlin.coroutines.Continuation): kotlin.Unit + +public final class Controller { + public constructor Controller() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final operator fun handleResult(/*0*/ x: kotlin.String, /*1*/ y: kotlin.coroutines.Continuation): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final suspend fun suspendHere(/*0*/ x: kotlin.coroutines.Continuation): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 8b43c9b8bce..5610acd614d 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -3915,6 +3915,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("unsupported.kt") + public void testUnsupported() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/coroutines/unsupported.kt"); + doTest(fileName); + } + @TestMetadata("wrongHandleResult.kt") public void testWrongHandleResult() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/coroutines/wrongHandleResult.kt");