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
+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);
}
};
}