NoArg: Add IDE integration

This commit is contained in:
Yan Zhulanow
2016-12-01 20:36:58 +03:00
committed by Yan Zhulanow
parent be26246a3e
commit c705f0c437
21 changed files with 414 additions and 19 deletions
@@ -24,21 +24,13 @@ import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.extensions.AnnotationBasedExtension
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtModifierListOwner
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.org.objectweb.asm.MethodVisitor
import org.jetbrains.org.objectweb.asm.Opcodes
class CliNoArgClassBuilderInterceptorExtension(
private val noArgAnnotationFqNames: List<String>
) : AbstractNoArgClassBuilderInterceptorExtension() {
override fun getAnnotationFqNames(modifierListOwner: KtModifierListOwner) = noArgAnnotationFqNames
}
abstract class AbstractNoArgClassBuilderInterceptorExtension : ClassBuilderInterceptorExtension, AnnotationBasedExtension {
class NoArgClassBuilderInterceptorExtension : ClassBuilderInterceptorExtension {
override fun interceptClassBuilderFactory(
interceptedFactory: ClassBuilderFactory,
bindingContext: BindingContext,
@@ -96,12 +88,14 @@ abstract class AbstractNoArgClassBuilderInterceptorExtension : ClassBuilderInter
if (origin is KtClass) {
val descriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, origin] as? ClassDescriptor
if (descriptor != null && descriptor.kind == ClassKind.CLASS && descriptor.hasSpecialAnnotation (origin)) {
if (descriptor != null && descriptor.kind == ClassKind.CLASS && origin.isNoArgClass()) {
hasSpecialAnnotation = true
}
}
}
private fun KtClass.isNoArgClass() = this.getUserData(NO_ARG_CLASS_KEY) ?: false
override fun newMethod(
origin: JvmDeclarationOrigin,
access: Int,
@@ -0,0 +1,21 @@
/*
* Copyright 2010-2016 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.
*/
package org.jetbrains.kotlin.noarg
import com.intellij.openapi.util.Key
val NO_ARG_CLASS_KEY = Key<Boolean>("org.jetbrains.kotlin.noarg.NoArgClassKey")
+22 -2
View File
@@ -17,6 +17,8 @@
package org.jetbrains.kotlin.noarg
import com.intellij.mock.MockProject
import com.intellij.openapi.extensions.Extensions
import org.jetbrains.kotlin.noarg.diagnostic.DefaultErrorMessagesNoArg
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
import org.jetbrains.kotlin.compiler.plugin.CliOption
import org.jetbrains.kotlin.compiler.plugin.CliOptionProcessingException
@@ -24,6 +26,13 @@ import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.CompilerConfigurationKey
import org.jetbrains.kotlin.container.StorageComponentContainer
import org.jetbrains.kotlin.container.useInstance
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
import org.jetbrains.kotlin.noarg.diagnostic.CliNoArgDeclarationChecker
import org.jetbrains.kotlin.resolve.TargetPlatform
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
object NoArgConfigurationKeys {
val ANNOTATION: CompilerConfigurationKey<List<String>> =
@@ -38,7 +47,7 @@ class NoArgCommandLineProcessor : CommandLineProcessor {
required = false, allowMultipleOccurrences = true)
}
override val pluginId = "org.jetbrains.kotlin.noarg"
override val pluginId = PLUGIN_ID
override val pluginOptions = listOf(ANNOTATION_OPTION)
override fun processOption(option: CliOption, value: String, configuration: CompilerConfiguration) = when (option) {
@@ -56,6 +65,17 @@ class NoArgComponentRegistrar : ComponentRegistrar {
val annotations = configuration.get(NoArgConfigurationKeys.ANNOTATION) ?: return
if (annotations.isEmpty()) return
ClassBuilderInterceptorExtension.registerExtension(project, CliNoArgClassBuilderInterceptorExtension(annotations))
Extensions.getRootArea().getExtensionPoint(DefaultErrorMessages.Extension.EP_NAME).registerExtension(DefaultErrorMessagesNoArg())
StorageComponentContainerContributor.registerExtension(project, CliNoArgComponentContainerContributor(annotations))
ClassBuilderInterceptorExtension.registerExtension(project, NoArgClassBuilderInterceptorExtension())
}
}
class CliNoArgComponentContainerContributor(val annotations: List<String>) : StorageComponentContainerContributor {
override fun addDeclarations(container: StorageComponentContainer, platform: TargetPlatform) {
if (platform is JvmPlatform) {
container.useInstance(CliNoArgDeclarationChecker(annotations))
}
}
}
@@ -0,0 +1,63 @@
/*
* Copyright 2010-2016 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.
*/
package org.jetbrains.kotlin.noarg.diagnostic
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.extensions.AnnotationBasedExtension
import org.jetbrains.kotlin.noarg.NO_ARG_CLASS_KEY
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtModifierListOwner
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue
class CliNoArgDeclarationChecker(val noArgAnnotationFqNames: List<String>) : AbstractNoArgDeclarationChecker() {
override fun getAnnotationFqNames(modifierListOwner: KtModifierListOwner) = noArgAnnotationFqNames
}
abstract class AbstractNoArgDeclarationChecker : DeclarationChecker, AnnotationBasedExtension {
override fun check(
declaration: KtDeclaration,
descriptor: DeclarationDescriptor,
diagnosticHolder: DiagnosticSink,
bindingContext: BindingContext,
languageVersionSettings: LanguageVersionSettings
) {
// Handle only classes
if (descriptor !is ClassDescriptor || declaration !is KtClass) return
if (descriptor.kind != ClassKind.CLASS) return
val hasSpecialAnnotation = descriptor.hasSpecialAnnotation(declaration)
declaration.putUserData(NO_ARG_CLASS_KEY, hasSpecialAnnotation)
if (hasSpecialAnnotation) {
val superClass = descriptor.getSuperClassOrAny()
if (superClass.constructors.none { it.isNoArgConstructor() } && !superClass.hasSpecialAnnotation(declaration)) {
val reportTarget = declaration.nameIdentifier ?: declaration.getClassOrInterfaceKeyword() ?: declaration
diagnosticHolder.report(ErrorsNoArg.NO_NOARG_CONSTRUCTOR_IN_SUPERCLASS.on(reportTarget))
}
}
}
private fun ConstructorDescriptor.isNoArgConstructor() = (valueParameters.isEmpty()) || valueParameters.all { it.hasDefaultValue() }
}
@@ -0,0 +1,32 @@
/*
* Copyright 2010-2016 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.
*/
package org.jetbrains.kotlin.noarg.diagnostic
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticFactoryToRendererMap
class DefaultErrorMessagesNoArg : DefaultErrorMessages.Extension {
private companion object {
val MAP = DiagnosticFactoryToRendererMap("AnnotationProcessing")
init {
MAP.put(ErrorsNoArg.NO_NOARG_CONSTRUCTOR_IN_SUPERCLASS, "Zero-argument constructor was not found in the superclass")
}
}
override fun getMap() = MAP
}
@@ -0,0 +1,35 @@
/*
* Copyright 2010-2016 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.
*/
package org.jetbrains.kotlin.noarg.diagnostic;
import com.intellij.psi.PsiElement;
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0;
import org.jetbrains.kotlin.diagnostics.Errors;
import static org.jetbrains.kotlin.diagnostics.Severity.WARNING;
public interface ErrorsNoArg {
DiagnosticFactory0<PsiElement> NO_NOARG_CONSTRUCTOR_IN_SUPERCLASS = DiagnosticFactory0.create(WARNING);
@SuppressWarnings("UnusedDeclaration")
Object _initializer = new Object() {
{
Errors.Initializer.initializeFactoryNames(ErrorsNoArg.class);
}
};
}