diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index b3bd34cce05..5aea91f0f45 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -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.diagnostics; @@ -27,7 +16,6 @@ import org.jetbrains.kotlin.config.LanguageFeature; import org.jetbrains.kotlin.config.LanguageVersion; import org.jetbrains.kotlin.config.LanguageVersionSettings; import org.jetbrains.kotlin.descriptors.*; -import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticRenderer; import org.jetbrains.kotlin.lexer.KtKeywordToken; import org.jetbrains.kotlin.lexer.KtModifierKeywordToken; import org.jetbrains.kotlin.lexer.KtTokens; @@ -297,6 +285,15 @@ public interface Errors { DiagnosticFactory0 NON_PRIVATE_CONSTRUCTOR_IN_ENUM = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 NON_PRIVATE_CONSTRUCTOR_IN_SEALED = DiagnosticFactory0.create(ERROR); + // Inline classes + + DiagnosticFactory0 INLINE_CLASS_NOT_TOP_LEVEL = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 INLINE_CLASS_NOT_FINAL = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 ABSENCE_OF_PRIMARY_CONSTRUCTOR_FOR_INLINE_CLASS = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 INLINE_CLASS_CONSTRUCTOR_WRONG_PARAMETERS_SIZE = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 INLINE_CLASS_CONSTRUCTOR_NOT_FINAL_READ_ONLY_PARAMETER = DiagnosticFactory0.create(ERROR); + + // Secondary constructors DiagnosticFactory0 CYCLIC_CONSTRUCTOR_DELEGATION_CALL = DiagnosticFactory0.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 595fa9fdf06..bd2d1c1cdeb 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -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.diagnostics.rendering; @@ -614,6 +603,12 @@ public class DefaultErrorMessages { MAP.put(NON_PRIVATE_CONSTRUCTOR_IN_ENUM, "Constructor must be private in enum class"); MAP.put(NON_PRIVATE_CONSTRUCTOR_IN_SEALED, "Constructor must be private in sealed class"); + MAP.put(INLINE_CLASS_NOT_TOP_LEVEL, "Inline classes are only allowed on top level"); + MAP.put(INLINE_CLASS_NOT_FINAL, "Inline classes can be only final"); + MAP.put(ABSENCE_OF_PRIMARY_CONSTRUCTOR_FOR_INLINE_CLASS, "Primary constructor is required for inline class"); + MAP.put(INLINE_CLASS_CONSTRUCTOR_WRONG_PARAMETERS_SIZE, "Inline class must have exactly one primary constructor parameter"); + MAP.put(INLINE_CLASS_CONSTRUCTOR_NOT_FINAL_READ_ONLY_PARAMETER, "Inline class primary constructor must have only final read-only (val) property parameter"); + MAP.put(VARIANCE_ON_TYPE_PARAMETER_NOT_ALLOWED, "Variance annotations are only allowed for type parameters of classes and interfaces"); MAP.put(BOUND_ON_TYPE_ALIAS_PARAMETER_NOT_ALLOWED, "Bounds are not allowed on type alias parameters"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt index 656434d5632..53a6bcd4da7 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.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.resolve @@ -91,7 +80,8 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf( DynamicReceiverChecker, DelegationChecker(), KClassWithIncorrectTypeArgumentChecker, - SuspendOperatorsCheckers + SuspendOperatorsCheckers, + InlineClassDeclarationChecker ) private val DEFAULT_CALL_CHECKERS = listOf( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineClassDeclarationChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineClassDeclarationChecker.kt new file mode 100644 index 00000000000..a216bc04e46 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineClassDeclarationChecker.kt @@ -0,0 +1,65 @@ +/* + * 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.checkers + +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtClass +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtParameter +import org.jetbrains.kotlin.psi.psiUtil.modalityModifier +import org.jetbrains.kotlin.resolve.DescriptorUtils + +object InlineClassDeclarationChecker : DeclarationChecker { + override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) { + if (!context.languageVersionSettings.supportsFeature(LanguageFeature.InlineClasses)) return + + if (declaration !is KtClass) return + if (descriptor !is ClassDescriptor || !descriptor.isInline) return + if (descriptor.kind != ClassKind.CLASS) return + + val trace = context.trace + if (!DescriptorUtils.isTopLevelDeclaration(descriptor)) { + trace.report(Errors.INLINE_CLASS_NOT_TOP_LEVEL.on(declaration)) + return + } + + val modalityModifier = declaration.modalityModifier() + if (modalityModifier != null && descriptor.modality != Modality.FINAL) { + trace.report(Errors.INLINE_CLASS_NOT_FINAL.on(modalityModifier)) + return + } + + val primaryConstructor = declaration.primaryConstructor + if (primaryConstructor == null) { + trace.report(Errors.ABSENCE_OF_PRIMARY_CONSTRUCTOR_FOR_INLINE_CLASS.on(declaration)) + return + } + + val baseParameter = primaryConstructor.valueParameters.singleOrNull() + if (baseParameter == null) { + (primaryConstructor.valueParameterList ?: declaration).let { + trace.report(Errors.INLINE_CLASS_CONSTRUCTOR_WRONG_PARAMETERS_SIZE.on(it)) + return + } + } + + if (!isParameterAcceptableForInlineClass(baseParameter)) { + trace.report(Errors.INLINE_CLASS_CONSTRUCTOR_NOT_FINAL_READ_ONLY_PARAMETER.on(baseParameter)) + return + } + } + + private fun isParameterAcceptableForInlineClass(parameter: KtParameter): Boolean { + val isOpen = parameter.modalityModifier()?.node?.elementType == KtTokens.OPEN_KEYWORD + return parameter.hasValOrVar() && !parameter.isMutable && !parameter.isVarArg && !parameter.hasDefaultValue() && !isOpen + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inlineClasses/inlineClassDeclarationCheck.kt b/compiler/testData/diagnostics/tests/inlineClasses/inlineClassDeclarationCheck.kt new file mode 100644 index 00000000000..af375ab0807 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inlineClasses/inlineClassDeclarationCheck.kt @@ -0,0 +1,31 @@ +// !LANGUAGE: +InlineClasses +// !DIAGNOSTICS: -UNUSED_PARAMETER + +inline class A0(val x: Int) + +inline class A1 +inline class A2() +inline class A3(x: Int) +inline class A4(var x: Int) +inline class A5(val x: Int, val y: Int) +inline class A6(x: Int, val y: Int) +inline class A7(vararg val x: Int) +inline class A8(open val x: Int) +inline class A9(final val x: Int) + +class B1 { + companion object { + inline class C1(val x: Int) + } + + inline class C2(val x: Int) +} + +object B2 { + inline class C3(val x: Int) +} + +final inline class D0(val x: Int) +open inline class D1(val x: Int) +abstract inline class D2(val x: Int) +sealed inline class D3(val x: Int) diff --git a/compiler/testData/diagnostics/tests/inlineClasses/inlineClassDeclarationCheck.txt b/compiler/testData/diagnostics/tests/inlineClasses/inlineClassDeclarationCheck.txt new file mode 100644 index 00000000000..fedc5ba3ecf --- /dev/null +++ b/compiler/testData/diagnostics/tests/inlineClasses/inlineClassDeclarationCheck.txt @@ -0,0 +1,156 @@ +package + +public final inline class A0 { + public constructor A0(/*0*/ x: kotlin.Int) + public final val x: kotlin.Int + 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 +} + +public final inline class A1 { + public constructor A1() + 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 +} + +public final inline class A2 { + public constructor A2() + 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 +} + +public final inline class A3 { + public constructor A3(/*0*/ x: kotlin.Int) + 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 +} + +public final inline class A4 { + public constructor A4(/*0*/ x: kotlin.Int) + public final var x: kotlin.Int + 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 +} + +public final inline class A5 { + public constructor A5(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int) + public final val x: kotlin.Int + public final val y: kotlin.Int + 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 +} + +public final inline class A6 { + public constructor A6(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int) + public final val y: kotlin.Int + 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 +} + +public final inline class A7 { + public constructor A7(/*0*/ vararg x: kotlin.Int /*kotlin.IntArray*/) + public final val x: kotlin.IntArray + 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 +} + +public final inline class A8 { + public constructor A8(/*0*/ x: kotlin.Int) + public open val x: kotlin.Int + 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 +} + +public final inline class A9 { + public constructor A9(/*0*/ x: kotlin.Int) + public final val x: kotlin.Int + 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 +} + +public final class B1 { + public constructor B1() + 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 + + public final inline class C2 { + public constructor C2(/*0*/ x: kotlin.Int) + public final val x: kotlin.Int + 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 + } + + public companion object Companion { + private constructor Companion() + 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 + + public final inline class C1 { + public constructor C1(/*0*/ x: kotlin.Int) + public final val x: kotlin.Int + 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 + } + } +} + +public object B2 { + private constructor B2() + 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 + + public final inline class C3 { + public constructor C3(/*0*/ x: kotlin.Int) + public final val x: kotlin.Int + 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 + } +} + +public final inline class D0 { + public constructor D0(/*0*/ x: kotlin.Int) + public final val x: kotlin.Int + 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 +} + +public open inline class D1 { + public constructor D1(/*0*/ x: kotlin.Int) + public final val x: kotlin.Int + 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 +} + +public abstract inline class D2 { + public constructor D2(/*0*/ x: kotlin.Int) + public final val x: kotlin.Int + 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 +} + +public sealed inline class D3 { + private constructor D3(/*0*/ x: kotlin.Int) + public final val x: kotlin.Int + 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 21a73c2644b..edab56422e5 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -12082,6 +12082,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inlineClasses/basicInlineClassDeclarationDisabled.kt"); doTest(fileName); } + + @TestMetadata("inlineClassDeclarationCheck.kt") + public void testInlineClassDeclarationCheck() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inlineClasses/inlineClassDeclarationCheck.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/diagnostics/tests/inner") diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index cea86cc2294..f832a168af9 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -12082,6 +12082,12 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inlineClasses/basicInlineClassDeclarationDisabled.kt"); doTest(fileName); } + + @TestMetadata("inlineClassDeclarationCheck.kt") + public void testInlineClassDeclarationCheck() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inlineClasses/inlineClassDeclarationCheck.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/diagnostics/tests/inner")