diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationChecker.kt index c03bf82d761..d442865996f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationChecker.kt @@ -1,23 +1,11 @@ /* - * 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. + * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. */ package org.jetbrains.kotlin.resolve import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor @@ -154,15 +142,11 @@ class AnnotationChecker( } } - fun checkWithUseSiteTargets(additionalTarget: KotlinTarget? = null, useDeprecatedTargets: Boolean = false): Boolean { + fun checkWithUseSiteTargets(): Boolean { if (useSiteTarget == null) return false - val useSiteMapping = KotlinTarget.USE_SITE_MAPPING[useSiteTarget].let { - if (useDeprecatedTargets && it == RECEIVER) KotlinTarget.VALUE_PARAMETER else it - } - - val allTargets = actualTargets.onlyWithUseSiteTarget + listOfNotNull(additionalTarget) - return allTargets.any { it in applicableTargets && it == useSiteMapping } + val useSiteMapping = KotlinTarget.USE_SITE_MAPPING[useSiteTarget] + return actualTargets.onlyWithUseSiteTarget.any { it in applicableTargets && it == useSiteMapping } } if (check(actualTargets.defaultTargets) || check(actualTargets.canBeSubstituted) || checkWithUseSiteTargets()) { @@ -170,14 +154,6 @@ class AnnotationChecker( return } - if (!languageVersionSettings.supportsFeature(LanguageFeature.RestrictionOfWrongAnnotationsWithUseSiteTargetsOnTypes)) { - val isAnnotationOnType = TargetLists.T_TYPE_REFERENCE == actualTargets - if (isAnnotationOnType && checkWithUseSiteTargets(KotlinTarget.VALUE_PARAMETER, true)) { - trace.report(Errors.WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET_ON_TYPE.on(entry, useSiteTarget!!.renderName)) - return - } - } - if (useSiteTarget != null) { trace.report( Errors.WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET.on( @@ -350,7 +326,7 @@ class AnnotationChecker( val T_OBJECT_LITERAL = targetList(OBJECT_LITERAL, CLASS, EXPRESSION) val T_TYPE_REFERENCE = targetList(TYPE) { - onlyWithUseSiteTarget(RECEIVER) + onlyWithUseSiteTarget(VALUE_PARAMETER) } val T_TYPE_PARAMETER = targetList(TYPE_PARAMETER) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationUseSiteTargetChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationUseSiteTargetChecker.kt index 64b4c6b9c1e..a97e8e511e6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationUseSiteTargetChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationUseSiteTargetChecker.kt @@ -1,21 +1,12 @@ /* - * 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. + * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. */ package org.jetbrains.kotlin.resolve +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.descriptors.ConstructorDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor @@ -26,14 +17,60 @@ import org.jetbrains.kotlin.psi.* object AnnotationUseSiteTargetChecker { - fun check(annotated: KtAnnotated, descriptor: DeclarationDescriptor, trace: BindingTrace) { + fun check( + annotated: KtAnnotated, + descriptor: DeclarationDescriptor, + trace: BindingTrace, + languageVersionSettings: LanguageVersionSettings + ) { trace.checkDeclaration(annotated, descriptor) + if (annotated is KtCallableDeclaration) { + annotated.receiverTypeReference?.let { trace.checkTypeReference(it, languageVersionSettings, isReceiver = true) } + annotated.typeReference?.let { trace.checkTypeReference(it, languageVersionSettings, isReceiver = false) } + } + if (annotated is KtFunction) { for (parameter in annotated.valueParameters) { if (parameter.hasValOrVar()) continue val parameterDescriptor = trace.bindingContext[BindingContext.VALUE_PARAMETER, parameter] ?: continue + trace.checkDeclaration(parameter, parameterDescriptor) + parameter.typeReference?.let { trace.checkTypeReference(it, languageVersionSettings, isReceiver = false) } + } + } + } + + private fun BindingTrace.checkTypeReference( + topLevelTypeReference: KtTypeReference, + languageVersionSettings: LanguageVersionSettings, + isReceiver: Boolean + ) { + checkAsTopLevelTypeReference(topLevelTypeReference, languageVersionSettings, isReceiver) + + topLevelTypeReference.acceptChildren( + typeReferenceRecursiveVisitor { typeReference -> + checkAsTopLevelTypeReference(typeReference, languageVersionSettings, isReceiver = false) + } + ) + } + + private fun BindingTrace.checkAsTopLevelTypeReference( + topLevelTypeReference: KtTypeReference, + languageVersionSettings: LanguageVersionSettings, + isReceiver: Boolean + ) { + for (annotationEntry in topLevelTypeReference.annotationEntries) { + val target = annotationEntry.useSiteTarget?.getAnnotationUseSiteTarget() ?: continue + + if (target != AnnotationUseSiteTarget.RECEIVER || !isReceiver) { + val diagnostic = + if (languageVersionSettings.supportsFeature(LanguageFeature.RestrictionOfWrongAnnotationsWithUseSiteTargetsOnTypes)) + WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET.on(annotationEntry, "undefined target", target.renderName) + else + WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET_ON_TYPE.on(annotationEntry, target.renderName) + + reportDiagnosticOnce(diagnostic) } } } @@ -64,8 +101,9 @@ object AnnotationUseSiteTargetChecker { } AnnotationUseSiteTarget.SETTER_PARAMETER -> checkIfMutableProperty(annotated, annotation) AnnotationUseSiteTarget.FILE -> reportDiagnosticOnce(INAPPLICABLE_FILE_TARGET.on(useSiteTarget)) - AnnotationUseSiteTarget.RECEIVER -> { - } + AnnotationUseSiteTarget.RECEIVER -> + // annotation with use-site target `receiver` can be only on type reference, but not on declaration + reportDiagnosticOnce(WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET.on(annotation, "declaration", target.renderName)) } } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java index 39909abaec7..066dabe405a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java @@ -1,17 +1,6 @@ /* - * Copyright 2010-2015 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. + * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. */ package org.jetbrains.kotlin.resolve; @@ -222,7 +211,7 @@ public class ModifiersChecker { } private void checkModifierListCommon(@NotNull KtDeclaration modifierListOwner, @NotNull DeclarationDescriptor descriptor) { - AnnotationUseSiteTargetChecker.INSTANCE.check(modifierListOwner, descriptor, trace); + AnnotationUseSiteTargetChecker.INSTANCE.check(modifierListOwner, descriptor, trace, languageVersionSettings); runDeclarationCheckers(modifierListOwner, descriptor); annotationChecker.check(modifierListOwner, trace, descriptor); ModifierCheckerCore.INSTANCE.check(modifierListOwner, trace, descriptor, languageVersionSettings); diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/VisitorWrappers.kt b/compiler/psi/src/org/jetbrains/kotlin/psi/VisitorWrappers.kt index bf88aa507e4..021c2e0af74 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/psi/VisitorWrappers.kt +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/VisitorWrappers.kt @@ -170,6 +170,14 @@ fun prefixExpressionRecursiveVisitor(block: (KtPrefixExpression) -> Unit) = } } +fun typeReferenceRecursiveVisitor(block: (KtTypeReference) -> Unit) = + object : KtTreeVisitorVoid() { + override fun visitTypeReference(typeReference: KtTypeReference) { + super.visitTypeReference(typeReference) + block(typeReference) + } + } + fun namedFunctionVisitor(block: (KtNamedFunction) -> Unit) = object : KtVisitorVoid() { override fun visitNamedFunction(namedFunction: KtNamedFunction) { diff --git a/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/ReceiverAnnotations.kt b/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/ReceiverAnnotations.kt index 1fac1cfafac..e1bc69caa75 100644 --- a/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/ReceiverAnnotations.kt +++ b/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/ReceiverAnnotations.kt @@ -1,9 +1,9 @@ annotation class Ann -@receiver:Ann +@receiver:Ann class SomeClass { - @receiver:Ann + @receiver:Ann constructor(@receiver:Ann a: String) @receiver:Ann diff --git a/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/fileAnnotationWithoutColon.kt b/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/fileAnnotationWithoutColon.kt index 18c65e57c2e..746004b3f08 100644 --- a/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/fileAnnotationWithoutColon.kt +++ b/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/fileAnnotationWithoutColon.kt @@ -5,6 +5,6 @@ fun test1(@@file @Suppress("") fun test2() {} -class OnType(x: @file Suppress("") Int) +class OnType(x: @file Suppress("") Int) -fun @file : Suppress("") Int.test3() {} \ No newline at end of file +fun @file : Suppress("") Int.test3() {} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/receiverUseSiteTargetOnExtensionFunction.kt b/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/receiverUseSiteTargetOnExtensionFunction.kt new file mode 100644 index 00000000000..2c8e6298537 --- /dev/null +++ b/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/receiverUseSiteTargetOnExtensionFunction.kt @@ -0,0 +1,8 @@ +@Target(AnnotationTarget.VALUE_PARAMETER) +annotation class Fancy + +fun @receiver:Fancy String.myExtension() { } +val @receiver:Fancy Int.asVal get() = 0 + +fun ((@receiver:Fancy Int) -> Unit).complexReceiver1() {} +fun ((Int) -> @receiver:Fancy Unit).complexReceiver2() {} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/receiverUseSiteTargetOnExtensionFunction.txt b/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/receiverUseSiteTargetOnExtensionFunction.txt new file mode 100644 index 00000000000..4036b4ab0c4 --- /dev/null +++ b/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/receiverUseSiteTargetOnExtensionFunction.txt @@ -0,0 +1,13 @@ +package + +public val @receiver:Fancy kotlin.Int.asVal: kotlin.Int +public fun ((@receiver:Fancy kotlin.Int) -> kotlin.Unit).complexReceiver1(): kotlin.Unit +public fun ((kotlin.Int) -> @receiver:Fancy kotlin.Unit).complexReceiver2(): kotlin.Unit +public fun @receiver:Fancy kotlin.String.myExtension(): kotlin.Unit + +@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.VALUE_PARAMETER}) public final annotation class Fancy : kotlin.Annotation { + public constructor Fancy() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + 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 5087109700c..7fa73c5fabc 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -1678,6 +1678,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("receiverUseSiteTargetOnExtensionFunction.kt") + public void testReceiverUseSiteTargetOnExtensionFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/receiverUseSiteTargetOnExtensionFunction.kt"); + doTest(fileName); + } + @TestMetadata("repeatable.kt") public void testRepeatable() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/repeatable.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index 3cc2dc981ee..2ff36d9a83c 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -1678,6 +1678,12 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing doTest(fileName); } + @TestMetadata("receiverUseSiteTargetOnExtensionFunction.kt") + public void testReceiverUseSiteTargetOnExtensionFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/receiverUseSiteTargetOnExtensionFunction.kt"); + doTest(fileName); + } + @TestMetadata("repeatable.kt") public void testRepeatable() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/repeatable.kt"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/KotlinTarget.kt b/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/KotlinTarget.kt index c8f8c5c697a..a97fdf31487 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/KotlinTarget.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/KotlinTarget.kt @@ -1,17 +1,6 @@ /* - * 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. + * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. */ package org.jetbrains.kotlin.descriptors.annotations @@ -40,8 +29,6 @@ enum class KotlinTarget(val description: String, val isDefault: Boolean = true) FILE("file", false), TYPEALIAS("typealias", false), - RECEIVER("receiver", true), - TYPE_PROJECTION("type projection", false), STAR_PROJECTION("star projection", false), PROPERTY_PARAMETER("property constructor parameter", false), @@ -126,7 +113,7 @@ enum class KotlinTarget(val description: String, val isDefault: Boolean = true) AnnotationUseSiteTarget.FILE to FILE, AnnotationUseSiteTarget.PROPERTY_GETTER to PROPERTY_GETTER, AnnotationUseSiteTarget.PROPERTY_SETTER to PROPERTY_SETTER, - AnnotationUseSiteTarget.RECEIVER to RECEIVER, + AnnotationUseSiteTarget.RECEIVER to VALUE_PARAMETER, AnnotationUseSiteTarget.SETTER_PARAMETER to VALUE_PARAMETER, AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD to FIELD) diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddAnnotationTargetFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddAnnotationTargetFix.kt index 9b866e44c94..b963290b9df 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddAnnotationTargetFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddAnnotationTargetFix.kt @@ -1,17 +1,6 @@ /* - * 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. + * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. */ package org.jetbrains.kotlin.idea.quickfix @@ -21,12 +10,10 @@ import com.intellij.openapi.project.Project import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.searches.ReferencesSearch import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.idea.caches.resolve.analyze -import org.jetbrains.kotlin.idea.project.languageVersionSettings import org.jetbrains.kotlin.idea.search.restrictToKotlinSources import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType @@ -103,13 +90,7 @@ private fun KtAnnotationEntry.getActualTargetList(): List { } val target = KotlinTarget.USE_SITE_MAPPING[useSiteTarget?.getAnnotationUseSiteTarget()] ?: return emptyList() if (target !in with(targetList) { defaultTargets + canBeSubstituted + onlyWithUseSiteTarget }) return emptyList() - return if (target == KotlinTarget.RECEIVER && - !languageVersionSettings.supportsFeature(LanguageFeature.RestrictionOfWrongAnnotationsWithUseSiteTargetsOnTypes) - ) { - listOf(KotlinTarget.VALUE_PARAMETER) - } else { - listOf(target) - } + return listOf(target) } private fun KtClass.addAnnotationTargets(annotationTargets: List, psiFactory: KtPsiFactory) {