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:
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
+23
-7
@@ -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)
|
||||
}
|
||||
|
||||
+11
-1
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Generated
+40
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user