Noarg: prohibit noarg for inner and local classes

Report warning if old JVM backend is used, and error for JVM IR, which
is supposed to be enabled as default in the next Kotlin release.

 #KT-43725 Fixed
This commit is contained in:
Alexander Udalov
2020-11-26 17:44:29 +01:00
parent a343fffe9e
commit a06bffc4b9
13 changed files with 177 additions and 21 deletions
@@ -166,8 +166,7 @@ import org.jetbrains.kotlin.nj2k.AbstractTextNewJavaToKotlinCopyPasteConversionT
import org.jetbrains.kotlin.nj2k.inference.common.AbstractCommonConstraintCollectorTest
import org.jetbrains.kotlin.nj2k.inference.mutability.AbstractMutabilityInferenceTest
import org.jetbrains.kotlin.nj2k.inference.nullability.AbstractNullabilityInferenceTest
import org.jetbrains.kotlin.noarg.AbstractBlackBoxCodegenTestForNoArg
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
import org.jetbrains.kotlin.noarg.*
import org.jetbrains.kotlin.parcelize.test.AbstractParcelizeBoxTest
import org.jetbrains.kotlin.parcelize.test.AbstractParcelizeBytecodeListingTest
import org.jetbrains.kotlin.parcelize.test.AbstractParcelizeIrBoxTest
@@ -1664,6 +1663,8 @@ fun main(args: Array<String>) {
}
testGroup("plugins/noarg/noarg-cli/test", "plugins/noarg/noarg-cli/testData") {
testClass<AbstractDiagnosticsTestForNoArg> { model("diagnostics", extension = "kt") }
testClass<AbstractBytecodeListingTestForNoArg> {
model("bytecodeListing", extension = "kt")
}
@@ -146,8 +146,7 @@ import org.jetbrains.kotlin.nj2k.AbstractTextNewJavaToKotlinCopyPasteConversionT
import org.jetbrains.kotlin.nj2k.inference.common.AbstractCommonConstraintCollectorTest
import org.jetbrains.kotlin.nj2k.inference.mutability.AbstractMutabilityInferenceTest
import org.jetbrains.kotlin.nj2k.inference.nullability.AbstractNullabilityInferenceTest
import org.jetbrains.kotlin.noarg.AbstractBlackBoxCodegenTestForNoArg
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
import org.jetbrains.kotlin.noarg.*
import org.jetbrains.kotlin.parcelize.test.AbstractParcelizeBoxTest
import org.jetbrains.kotlin.parcelize.test.AbstractParcelizeBytecodeListingTest
import org.jetbrains.kotlin.parcelize.test.AbstractParcelizeIrBoxTest
@@ -1188,6 +1187,8 @@ fun main(args: Array<String>) {
}
testGroup("plugins/noarg/noarg-cli/test", "plugins/noarg/noarg-cli/testData") {
testClass<AbstractDiagnosticsTestForNoArg> { model("diagnostics", extension = "kt") }
testClass<AbstractBytecodeListingTestForNoArg> {
model("bytecodeListing", extension = "kt")
}
@@ -149,8 +149,7 @@ import org.jetbrains.kotlin.nj2k.AbstractTextNewJavaToKotlinCopyPasteConversionT
import org.jetbrains.kotlin.nj2k.inference.common.AbstractCommonConstraintCollectorTest
import org.jetbrains.kotlin.nj2k.inference.mutability.AbstractMutabilityInferenceTest
import org.jetbrains.kotlin.nj2k.inference.nullability.AbstractNullabilityInferenceTest
import org.jetbrains.kotlin.noarg.AbstractBlackBoxCodegenTestForNoArg
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
import org.jetbrains.kotlin.noarg.*
import org.jetbrains.kotlin.psi.patternMatching.AbstractPsiUnifierTest
import org.jetbrains.kotlin.samWithReceiver.AbstractSamWithReceiverScriptTest
import org.jetbrains.kotlin.samWithReceiver.AbstractSamWithReceiverTest
@@ -1112,6 +1111,8 @@ fun main(args: Array<String>) {
}
testGroup("plugins/noarg/noarg-cli/test", "plugins/noarg/noarg-cli/testData") {
testClass<AbstractDiagnosticsTestForNoArg> { model("diagnostics", extension = "kt") }
testClass<AbstractBytecodeListingTestForNoArg> {
model("bytecodeListing", extension = "kt")
}
+11 -5
View File
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
import org.jetbrains.kotlin.compiler.plugin.*
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.CompilerConfigurationKey
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.container.StorageComponentContainer
import org.jetbrains.kotlin.container.useInstance
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
@@ -88,24 +89,29 @@ class NoArgComponentRegistrar : ComponentRegistrar {
SUPPORTED_PRESETS[preset]?.let { annotations += it }
}
if (annotations.isNotEmpty()) {
registerNoArgComponents(project, annotations, configuration.getBoolean(INVOKE_INITIALIZERS))
registerNoArgComponents(
project, annotations, configuration.getBoolean(JVMConfigurationKeys.IR), configuration.getBoolean(INVOKE_INITIALIZERS),
)
}
}
internal companion object {
fun registerNoArgComponents(project: Project, annotations: List<String>, invokeInitializers: Boolean) {
StorageComponentContainerContributor.registerExtension(project, CliNoArgComponentContainerContributor(annotations))
fun registerNoArgComponents(project: Project, annotations: List<String>, useIr: Boolean, invokeInitializers: Boolean) {
StorageComponentContainerContributor.registerExtension(project, CliNoArgComponentContainerContributor(annotations, useIr))
ExpressionCodegenExtension.registerExtension(project, CliNoArgExpressionCodegenExtension(annotations, invokeInitializers))
}
}
}
class CliNoArgComponentContainerContributor(val annotations: List<String>) : StorageComponentContainerContributor {
private class CliNoArgComponentContainerContributor(
private val annotations: List<String>,
private val useIr: Boolean,
) : StorageComponentContainerContributor {
override fun registerModuleComponents(
container: StorageComponentContainer, platform: TargetPlatform, moduleDescriptor: ModuleDescriptor
) {
if (!platform.isJvm()) return
container.useInstance(CliNoArgDeclarationChecker(annotations))
container.useInstance(CliNoArgDeclarationChecker(annotations, useIr))
}
}
@@ -16,32 +16,48 @@
package org.jetbrains.kotlin.noarg.diagnostic
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.extensions.AnnotationBasedExtension
import org.jetbrains.kotlin.noarg.diagnostic.ErrorsNoArg.*
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtModifierListOwner
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
class CliNoArgDeclarationChecker(private val noArgAnnotationFqNames: List<String>) : AbstractNoArgDeclarationChecker() {
override fun getAnnotationFqNames(modifierListOwner: KtModifierListOwner?) = noArgAnnotationFqNames
internal class CliNoArgDeclarationChecker(
private val noArgAnnotationFqNames: List<String>,
useIr: Boolean,
) : AbstractNoArgDeclarationChecker(useIr) {
override fun getAnnotationFqNames(modifierListOwner: KtModifierListOwner?): List<String> = noArgAnnotationFqNames
}
abstract class AbstractNoArgDeclarationChecker : DeclarationChecker, AnnotationBasedExtension {
internal abstract class AbstractNoArgDeclarationChecker(private val useIr: Boolean) : DeclarationChecker, AnnotationBasedExtension {
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
// Handle only classes
if (descriptor !is ClassDescriptor || declaration !is KtClass) return
if (descriptor.kind != ClassKind.CLASS) return
if (!descriptor.hasSpecialAnnotation(declaration)) return
if (descriptor.isInner) {
val diagnostic = if (useIr) NOARG_ON_INNER_CLASS_ERROR else NOARG_ON_INNER_CLASS
context.trace.report(diagnostic.on(declaration.reportTarget))
} else if (DescriptorUtils.isLocal(descriptor)) {
val diagnostic = if (useIr) NOARG_ON_LOCAL_CLASS_ERROR else NOARG_ON_LOCAL_CLASS
context.trace.report(diagnostic.on(declaration.reportTarget))
}
val superClass = descriptor.getSuperClassOrAny()
if (superClass.constructors.none { it.isNoArgConstructor() } && !superClass.hasSpecialAnnotation(declaration)) {
val reportTarget = declaration.nameIdentifier ?: declaration.getClassOrInterfaceKeyword() ?: declaration
context.trace.report(ErrorsNoArg.NO_NOARG_CONSTRUCTOR_IN_SUPERCLASS.on(reportTarget))
context.trace.report(NO_NOARG_CONSTRUCTOR_IN_SUPERCLASS.on(declaration.reportTarget))
}
}
private fun ConstructorDescriptor.isNoArgConstructor() = valueParameters.all(ValueParameterDescriptor::declaresDefaultValue)
private val KtClass.reportTarget: PsiElement
get() = nameIdentifier ?: getClassOrInterfaceKeyword() ?: this
private fun ConstructorDescriptor.isNoArgConstructor(): Boolean =
valueParameters.all(ValueParameterDescriptor::declaresDefaultValue)
}
@@ -25,5 +25,15 @@ object DefaultErrorMessagesNoArg : DefaultErrorMessages.Extension {
init {
MAP.put(ErrorsNoArg.NO_NOARG_CONSTRUCTOR_IN_SUPERCLASS, "Zero-argument constructor was not found in the superclass")
MAP.put(
ErrorsNoArg.NOARG_ON_INNER_CLASS,
"Noarg constructor generation for inner classes is deprecated and will be prohibited soon"
)
MAP.put(ErrorsNoArg.NOARG_ON_INNER_CLASS_ERROR, "Noarg constructor generation is not possible for inner classes")
MAP.put(
ErrorsNoArg.NOARG_ON_LOCAL_CLASS,
"Noarg constructor generation for local classes is deprecated and will be prohibited soon"
)
MAP.put(ErrorsNoArg.NOARG_ON_LOCAL_CLASS_ERROR, "Noarg constructor generation is not possible for local classes")
}
}
}
@@ -20,10 +20,15 @@ import com.intellij.psi.PsiElement;
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0;
import org.jetbrains.kotlin.diagnostics.Errors;
import static org.jetbrains.kotlin.diagnostics.Severity.ERROR;
import static org.jetbrains.kotlin.diagnostics.Severity.WARNING;
public interface ErrorsNoArg {
DiagnosticFactory0<PsiElement> NO_NOARG_CONSTRUCTOR_IN_SUPERCLASS = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<PsiElement> NOARG_ON_INNER_CLASS = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<PsiElement> NOARG_ON_INNER_CLASS_ERROR = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> NOARG_ON_LOCAL_CLASS = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<PsiElement> NOARG_ON_LOCAL_CLASS_ERROR = DiagnosticFactory0.create(ERROR);
@SuppressWarnings("UnusedDeclaration")
Object _initializer = new Object() {
@@ -31,5 +36,4 @@ public interface ErrorsNoArg {
Errors.Initializer.initializeFactoryNamesAndDefaultErrorMessages(ErrorsNoArg.class, DefaultErrorMessagesNoArg.INSTANCE);
}
};
}
@@ -0,0 +1,40 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.noarg;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.runner.RunWith;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("plugins/noarg/noarg-cli/testData/diagnostics")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class DiagnosticsTestForNoArgGenerated extends AbstractDiagnosticsTestForNoArg {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInDiagnostics() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/noarg/noarg-cli/testData/diagnostics"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@TestMetadata("innerClass.kt")
public void testInnerClass() throws Exception {
runTest("plugins/noarg/noarg-cli/testData/diagnostics/innerClass.kt");
}
@TestMetadata("noNoargCtorInSuperclass.kt")
public void testNoNoargCtorInSuperclass() throws Exception {
runTest("plugins/noarg/noarg-cli/testData/diagnostics/noNoargCtorInSuperclass.kt");
}
}
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.noarg
import org.jetbrains.kotlin.checkers.AbstractDiagnosticsTest
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.codegen.AbstractBlackBoxCodegenTest
import org.jetbrains.kotlin.codegen.AbstractBytecodeListingTest
@@ -16,6 +17,7 @@ abstract class AbstractBlackBoxCodegenTestForNoArg : AbstractBlackBoxCodegenTest
NoArgComponentRegistrar.registerNoArgComponents(
myEnvironment.project,
NOARG_ANNOTATIONS,
backend.isIR,
files.any { it.directives.contains("INVOKE_INITIALIZERS") },
)
@@ -25,6 +27,12 @@ abstract class AbstractBlackBoxCodegenTestForNoArg : AbstractBlackBoxCodegenTest
abstract class AbstractBytecodeListingTestForNoArg : AbstractBytecodeListingTest() {
override fun setupEnvironment(environment: KotlinCoreEnvironment) {
NoArgComponentRegistrar.registerNoArgComponents(environment.project, NOARG_ANNOTATIONS, false)
NoArgComponentRegistrar.registerNoArgComponents(environment.project, NOARG_ANNOTATIONS, backend.isIR, false)
}
}
abstract class AbstractDiagnosticsTestForNoArg : AbstractDiagnosticsTest() {
override fun setupEnvironment(environment: KotlinCoreEnvironment) {
NoArgComponentRegistrar.registerNoArgComponents(environment.project, NOARG_ANNOTATIONS, backend.isIR, false)
}
}
@@ -0,0 +1,14 @@
annotation class NoArg
class Outer {
@NoArg
inner class <!NOARG_ON_INNER_CLASS!>Inner<!>(val b: Any)
}
fun local() {
@NoArg
class <!NOARG_ON_LOCAL_CLASS!>Local<!>(val l: Any) {
@NoArg
inner class <!NOARG_ON_INNER_CLASS!>InnerLocal<!>(val x: Any)
}
}
@@ -0,0 +1,25 @@
package
public fun local(): kotlin.Unit
public final annotation class NoArg : kotlin.Annotation {
public constructor NoArg()
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 Outer {
public constructor Outer()
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
@NoArg public final inner class Inner {
public constructor Inner(/*0*/ b: kotlin.Any)
public final val b: kotlin.Any
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
}
}
@@ -0,0 +1,6 @@
annotation class NoArg
open class Base(val s: String)
@NoArg
class <!NO_NOARG_CONSTRUCTOR_IN_SUPERCLASS!>Derived<!>(s: String) : Base(s)
@@ -0,0 +1,24 @@
package
public open class Base {
public constructor Base(/*0*/ s: kotlin.String)
public final val s: kotlin.String
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
}
@NoArg public final class Derived : Base {
public constructor Derived(/*0*/ s: kotlin.String)
public final override /*1*/ /*fake_override*/ val s: kotlin.String
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 annotation class NoArg : kotlin.Annotation {
public constructor NoArg()
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
}