Report errors from compiler plugins as compiler PLUGIN_ERRORs (KT-19311)

This commit is contained in:
Yan Zhulanow
2017-07-28 22:27:34 +03:00
parent 9a5a003d0a
commit c1600c9841
22 changed files with 147 additions and 135 deletions
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.android.parcel
import org.jetbrains.kotlin.android.parcel.serializers.ParcelSerializer
import org.jetbrains.kotlin.android.parcel.serializers.isParcelable
import org.jetbrains.kotlin.android.synthetic.diagnostic.DefaultErrorMessagesAndroid
import org.jetbrains.kotlin.android.synthetic.diagnostic.ErrorsAndroid
import org.jetbrains.kotlin.codegen.ClassBuilderMode
import org.jetbrains.kotlin.codegen.state.IncompatibleClassTracker
@@ -27,6 +28,7 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.diagnostics.reportFromPlugin
import org.jetbrains.kotlin.fileClasses.NoResolveFileClassesProvider
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
@@ -82,7 +84,7 @@ class ParcelableDeclarationChecker : SimpleDeclarationChecker {
if (method.isWriteToParcel() && declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) {
val reportElement = declaration.modifierList?.getModifier(KtTokens.OVERRIDE_KEYWORD) ?: declaration.nameIdentifier ?: declaration
diagnosticHolder.report(ErrorsAndroid.OVERRIDING_WRITE_TO_PARCEL_IS_NOT_ALLOWED.on(reportElement))
diagnosticHolder.reportFromPlugin(ErrorsAndroid.OVERRIDING_WRITE_TO_PARCEL_IS_NOT_ALLOWED.on(reportElement), DefaultErrorMessagesAndroid)
}
}
@@ -97,14 +99,16 @@ class ParcelableDeclarationChecker : SimpleDeclarationChecker {
&& (declaration.hasDelegate() || bindingContext[BindingContext.BACKING_FIELD_REQUIRED, property] == true)
&& !property.annotations.hasAnnotation(TRANSIENT_FQNAME)
) {
diagnosticHolder.report(ErrorsAndroid.PROPERTY_WONT_BE_SERIALIZED.on(declaration.nameIdentifier ?: declaration))
val reportElement = declaration.nameIdentifier ?: declaration
diagnosticHolder.reportFromPlugin(ErrorsAndroid.PROPERTY_WONT_BE_SERIALIZED.on(reportElement), DefaultErrorMessagesAndroid)
}
// @JvmName is not applicable to property so we can check just the descriptor name
if (property.name.asString() == "CREATOR" && property.findJvmFieldAnnotation() != null && containingClass.isCompanionObject) {
val outerClass = containingClass.containingDeclaration as? ClassDescriptor
if (outerClass != null && outerClass.isParcelize) {
diagnosticHolder.report(ErrorsAndroid.CREATOR_DEFINITION_IS_NOT_ALLOWED.on(declaration.nameIdentifier ?: declaration))
val reportElement = declaration.nameIdentifier ?: declaration
diagnosticHolder.reportFromPlugin(ErrorsAndroid.CREATOR_DEFINITION_IS_NOT_ALLOWED.on(reportElement), DefaultErrorMessagesAndroid)
}
}
}
@@ -119,33 +123,35 @@ class ParcelableDeclarationChecker : SimpleDeclarationChecker {
if (declaration !is KtClass || (declaration.isAnnotation() || declaration.isInterface())) {
val reportElement = (declaration as? KtClassOrObject)?.nameIdentifier ?: declaration
diagnosticHolder.report(ErrorsAndroid.PARCELABLE_SHOULD_BE_CLASS.on(reportElement))
diagnosticHolder.reportFromPlugin(ErrorsAndroid.PARCELABLE_SHOULD_BE_CLASS.on(reportElement), DefaultErrorMessagesAndroid)
return
}
if (declaration.isEnum()) {
val reportElement = (declaration as? KtClass)?.nameIdentifier ?: declaration
diagnosticHolder.report(ErrorsAndroid.PARCELABLE_SHOULD_BE_CLASS.on(reportElement))
diagnosticHolder.reportFromPlugin(ErrorsAndroid.PARCELABLE_SHOULD_BE_CLASS.on(reportElement), DefaultErrorMessagesAndroid)
return
}
val sealedOrAbstract = declaration.modifierList?.let { it.getModifier(KtTokens.ABSTRACT_KEYWORD) ?: it.getModifier(KtTokens.SEALED_KEYWORD) }
if (sealedOrAbstract != null) {
diagnosticHolder.report(ErrorsAndroid.PARCELABLE_SHOULD_BE_INSTANTIABLE.on(sealedOrAbstract))
diagnosticHolder.reportFromPlugin(ErrorsAndroid.PARCELABLE_SHOULD_BE_INSTANTIABLE.on(sealedOrAbstract), DefaultErrorMessagesAndroid)
}
if (declaration.isInner()) {
val reportElement = declaration.modifierList?.getModifier(KtTokens.INNER_KEYWORD) ?: declaration.nameIdentifier ?: declaration
diagnosticHolder.report(ErrorsAndroid.PARCELABLE_CANT_BE_INNER_CLASS.on(reportElement))
diagnosticHolder.reportFromPlugin(ErrorsAndroid.PARCELABLE_CANT_BE_INNER_CLASS.on(reportElement), DefaultErrorMessagesAndroid)
}
if (declaration.isLocal) {
diagnosticHolder.report(ErrorsAndroid.PARCELABLE_CANT_BE_LOCAL_CLASS.on(declaration.nameIdentifier ?: declaration))
val reportElement = declaration.nameIdentifier ?: declaration
diagnosticHolder.reportFromPlugin(ErrorsAndroid.PARCELABLE_CANT_BE_LOCAL_CLASS.on(reportElement), DefaultErrorMessagesAndroid)
}
val superTypes = TypeUtils.getAllSupertypes(descriptor.defaultType)
if (superTypes.none { it.constructor.declarationDescriptor?.fqNameSafe == ANDROID_PARCELABLE_CLASS_FQNAME }) {
diagnosticHolder.report(ErrorsAndroid.NO_PARCELABLE_SUPERTYPE.on(declaration.nameIdentifier ?: declaration))
val reportElement = declaration.nameIdentifier ?: declaration
diagnosticHolder.reportFromPlugin(ErrorsAndroid.NO_PARCELABLE_SUPERTYPE.on(reportElement), DefaultErrorMessagesAndroid)
}
for (supertypeEntry in declaration.superTypeListEntries) {
@@ -154,15 +160,17 @@ class ParcelableDeclarationChecker : SimpleDeclarationChecker {
val type = bindingContext[BindingContext.TYPE, supertypeEntry.typeReference] ?: continue
if (type.isParcelable()) {
val reportElement = supertypeEntry.byKeywordNode?.psi ?: delegateExpression
diagnosticHolder.report(ErrorsAndroid.PARCELABLE_DELEGATE_IS_NOT_ALLOWED.on(reportElement))
diagnosticHolder.reportFromPlugin(ErrorsAndroid.PARCELABLE_DELEGATE_IS_NOT_ALLOWED.on(reportElement), DefaultErrorMessagesAndroid)
}
}
val primaryConstructor = declaration.primaryConstructor
if (primaryConstructor == null && declaration.secondaryConstructors.isNotEmpty()) {
diagnosticHolder.report(ErrorsAndroid.PARCELABLE_SHOULD_HAVE_PRIMARY_CONSTRUCTOR.on(declaration.nameIdentifier ?: declaration))
val reportElement = declaration.nameIdentifier ?: declaration
diagnosticHolder.reportFromPlugin(ErrorsAndroid.PARCELABLE_SHOULD_HAVE_PRIMARY_CONSTRUCTOR.on(reportElement), DefaultErrorMessagesAndroid)
} else if (primaryConstructor != null && primaryConstructor.valueParameters.isEmpty()) {
diagnosticHolder.report(ErrorsAndroid.PARCELABLE_PRIMARY_CONSTRUCTOR_IS_EMPTY.on(declaration.nameIdentifier ?: declaration))
val reportElement = declaration.nameIdentifier ?: declaration
diagnosticHolder.reportFromPlugin(ErrorsAndroid.PARCELABLE_PRIMARY_CONSTRUCTOR_IS_EMPTY.on(reportElement), DefaultErrorMessagesAndroid)
}
val typeMapper = KotlinTypeMapper(
@@ -181,8 +189,9 @@ class ParcelableDeclarationChecker : SimpleDeclarationChecker {
private fun checkParcelableClassProperty(parameter: KtParameter, diagnosticHolder: DiagnosticSink, typeMapper: KotlinTypeMapper) {
if (!parameter.hasValOrVar()) {
diagnosticHolder.report(ErrorsAndroid.PARCELABLE_CONSTRUCTOR_PARAMETER_SHOULD_BE_VAL_OR_VAR.on(
parameter.nameIdentifier ?: parameter))
val reportElement = parameter.nameIdentifier ?: parameter
diagnosticHolder.reportFromPlugin(
ErrorsAndroid.PARCELABLE_CONSTRUCTOR_PARAMETER_SHOULD_BE_VAL_OR_VAR.on(reportElement), DefaultErrorMessagesAndroid)
}
val descriptor = typeMapper.bindingContext[BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, parameter] ?: return
@@ -196,8 +205,8 @@ class ParcelableDeclarationChecker : SimpleDeclarationChecker {
}
catch (e: IllegalArgumentException) {
// get() throws IllegalArgumentException on unknown types
diagnosticHolder.report(ErrorsAndroid.PARCELABLE_TYPE_NOT_SUPPORTED.on(
parameter.typeReference ?: parameter.nameIdentifier ?: parameter))
val reportElement = parameter.typeReference ?: parameter.nameIdentifier ?: parameter
diagnosticHolder.reportFromPlugin(ErrorsAndroid.PARCELABLE_TYPE_NOT_SUPPORTED.on(reportElement), DefaultErrorMessagesAndroid)
}
}
}
@@ -117,7 +117,6 @@ class AndroidComponentRegistrar : ComponentRegistrar {
ExpressionCodegenExtension.registerExtension(project, CliAndroidExtensionsExpressionCodegenExtension(isExperimental, globalCacheImpl))
StorageComponentContainerContributor.registerExtension(project, AndroidExtensionPropertiesComponentContainerContributor())
Extensions.getRootArea().getExtensionPoint(DefaultErrorMessages.Extension.EP_NAME).registerExtension(DefaultErrorMessagesAndroid())
ClassBuilderInterceptorExtension.registerExtension(project, CliAndroidOnDestroyClassBuilderInterceptorExtension(globalCacheImpl))
PackageFragmentProviderExtension.registerExtension(project, CliAndroidPackageFragmentProviderExtension(isExperimental))
}
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.android.synthetic.res.AndroidSyntheticProperty
import org.jetbrains.kotlin.android.synthetic.res.isErrorType
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.diagnostics.reportFromPlugin
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtExpression
@@ -50,7 +51,7 @@ class AndroidExtensionPropertiesCallChecker : CallChecker {
private fun DiagnosticSink.checkDeprecated(expression: KtExpression, packageDescriptor: AndroidSyntheticPackageFragmentDescriptor) {
if (packageDescriptor.packageData.isDeprecated) {
report(SYNTHETIC_DEPRECATED_PACKAGE.on(expression))
reportFromPlugin(SYNTHETIC_DEPRECATED_PACKAGE.on(expression), DefaultErrorMessagesAndroid)
}
}
@@ -59,7 +60,7 @@ class AndroidExtensionPropertiesCallChecker : CallChecker {
val type = property.errorType ?: return
val warning = if (type.contains('.')) SYNTHETIC_UNRESOLVED_WIDGET_TYPE else SYNTHETIC_INVALID_WIDGET_TYPE
report(warning.on(expression, type))
reportFromPlugin(warning.on(expression, type), DefaultErrorMessagesAndroid)
}
private fun DiagnosticSink.checkPartiallyDefinedResource(
@@ -72,7 +73,7 @@ class AndroidExtensionPropertiesCallChecker : CallChecker {
val expectedType = context.resolutionContext.expectedType
if (!TypeUtils.noExpectedType(expectedType) && !expectedType.isMarkedNullable && !expectedType.isFlexible()) {
report(UNSAFE_CALL_ON_PARTIALLY_DEFINED_RESOURCE.on(calleeExpression))
reportFromPlugin(UNSAFE_CALL_ON_PARTIALLY_DEFINED_RESOURCE.on(calleeExpression), DefaultErrorMessagesAndroid)
return
}
@@ -80,7 +81,7 @@ class AndroidExtensionPropertiesCallChecker : CallChecker {
val usage = outermostQualifiedExpression.parent
if (usage is KtDotQualifiedExpression && usage.receiverExpression == outermostQualifiedExpression) {
report(UNSAFE_CALL_ON_PARTIALLY_DEFINED_RESOURCE.on(calleeExpression))
reportFromPlugin(UNSAFE_CALL_ON_PARTIALLY_DEFINED_RESOURCE.on(calleeExpression), DefaultErrorMessagesAndroid)
}
}
@@ -20,70 +20,66 @@ import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticFactoryToRendererMap
import org.jetbrains.kotlin.diagnostics.rendering.Renderers
class DefaultErrorMessagesAndroid : DefaultErrorMessages.Extension {
private companion object {
val MAP = DiagnosticFactoryToRendererMap("Android")
init {
MAP.put(ErrorsAndroid.SYNTHETIC_INVALID_WIDGET_TYPE,
"Widget has an invalid type ''{0}''. Please specify the fully qualified widget class name in XML",
Renderers.TO_STRING)
MAP.put(ErrorsAndroid.SYNTHETIC_UNRESOLVED_WIDGET_TYPE,
"Widget has an unresolved type ''{0}'', and thus it was upcasted to ''android.view.View''",
Renderers.TO_STRING)
MAP.put(ErrorsAndroid.SYNTHETIC_DEPRECATED_PACKAGE,
"Use properties from the build variant packages")
MAP.put(ErrorsAndroid.UNSAFE_CALL_ON_PARTIALLY_DEFINED_RESOURCE,
"Potential NullPointerException. The resource is missing in some of layout versions")
MAP.put(ErrorsAndroid.PARCELABLE_SHOULD_BE_CLASS,
"'Parcelable' should be a class")
MAP.put(ErrorsAndroid.PARCELABLE_DELEGATE_IS_NOT_ALLOWED,
"Delegating 'Parcelable' is now allowed")
MAP.put(ErrorsAndroid.PARCELABLE_SHOULD_NOT_BE_ENUM_CLASS,
"'Parcelable' should not be a 'enum class'")
MAP.put(ErrorsAndroid.PARCELABLE_SHOULD_BE_INSTANTIABLE,
"'Parcelable' should not be a 'sealed' or 'abstract' class")
MAP.put(ErrorsAndroid.PARCELABLE_CANT_BE_INNER_CLASS,
"'Parcelable' can't be an inner class")
MAP.put(ErrorsAndroid.PARCELABLE_CANT_BE_LOCAL_CLASS,
"'Parcelable' can't be a local class")
MAP.put(ErrorsAndroid.NO_PARCELABLE_SUPERTYPE,
"No 'Parcelable' supertype")
MAP.put(ErrorsAndroid.PARCELABLE_SHOULD_HAVE_PRIMARY_CONSTRUCTOR,
"'Parcelable' should have a primary constructor")
MAP.put(ErrorsAndroid.PARCELABLE_PRIMARY_CONSTRUCTOR_IS_EMPTY,
"The primary constructor is empty, no data will be serialized to 'Parcel'")
MAP.put(ErrorsAndroid.PARCELABLE_CONSTRUCTOR_PARAMETER_SHOULD_BE_VAL_OR_VAR,
"'Parcelable' constructor parameter should be 'val' or 'var'")
MAP.put(ErrorsAndroid.PROPERTY_WONT_BE_SERIALIZED,
"Property would not be serialized into a 'Parcel'. Add '@Transient' annotation to it")
MAP.put(ErrorsAndroid.OVERRIDING_WRITE_TO_PARCEL_IS_NOT_ALLOWED,
"Overriding 'writeToParcel' is not allowed. Use 'Parceler' companion object instead.")
MAP.put(ErrorsAndroid.CREATOR_DEFINITION_IS_NOT_ALLOWED,
"'CREATOR' definition is not allowed. Use 'Parceler' companion object instead.")
MAP.put(ErrorsAndroid.PARCELABLE_TYPE_NOT_SUPPORTED,
"Type is not directly supported by 'Parcelize'. " +
"Annotate the parameter type with '@RawValue' if you want it to be serialized using 'writeValue()'")
}
}
object DefaultErrorMessagesAndroid : DefaultErrorMessages.Extension {
private val MAP = DiagnosticFactoryToRendererMap("Android")
override fun getMap() = MAP
init {
MAP.put(ErrorsAndroid.SYNTHETIC_INVALID_WIDGET_TYPE,
"Widget has an invalid type ''{0}''. Please specify the fully qualified widget class name in XML",
Renderers.TO_STRING)
MAP.put(ErrorsAndroid.SYNTHETIC_UNRESOLVED_WIDGET_TYPE,
"Widget has an unresolved type ''{0}'', and thus it was upcasted to ''android.view.View''",
Renderers.TO_STRING)
MAP.put(ErrorsAndroid.SYNTHETIC_DEPRECATED_PACKAGE,
"Use properties from the build variant packages")
MAP.put(ErrorsAndroid.UNSAFE_CALL_ON_PARTIALLY_DEFINED_RESOURCE,
"Potential NullPointerException. The resource is missing in some of layout versions")
MAP.put(ErrorsAndroid.PARCELABLE_SHOULD_BE_CLASS,
"'Parcelable' should be a class")
MAP.put(ErrorsAndroid.PARCELABLE_DELEGATE_IS_NOT_ALLOWED,
"Delegating 'Parcelable' is now allowed")
MAP.put(ErrorsAndroid.PARCELABLE_SHOULD_NOT_BE_ENUM_CLASS,
"'Parcelable' should not be a 'enum class'")
MAP.put(ErrorsAndroid.PARCELABLE_SHOULD_BE_INSTANTIABLE,
"'Parcelable' should not be a 'sealed' or 'abstract' class")
MAP.put(ErrorsAndroid.PARCELABLE_CANT_BE_INNER_CLASS,
"'Parcelable' can't be an inner class")
MAP.put(ErrorsAndroid.PARCELABLE_CANT_BE_LOCAL_CLASS,
"'Parcelable' can't be a local class")
MAP.put(ErrorsAndroid.NO_PARCELABLE_SUPERTYPE,
"No 'Parcelable' supertype")
MAP.put(ErrorsAndroid.PARCELABLE_SHOULD_HAVE_PRIMARY_CONSTRUCTOR,
"'Parcelable' should have a primary constructor")
MAP.put(ErrorsAndroid.PARCELABLE_PRIMARY_CONSTRUCTOR_IS_EMPTY,
"The primary constructor is empty, no data will be serialized to 'Parcel'")
MAP.put(ErrorsAndroid.PARCELABLE_CONSTRUCTOR_PARAMETER_SHOULD_BE_VAL_OR_VAR,
"'Parcelable' constructor parameter should be 'val' or 'var'")
MAP.put(ErrorsAndroid.PROPERTY_WONT_BE_SERIALIZED,
"Property would not be serialized into a 'Parcel'. Add '@Transient' annotation to it")
MAP.put(ErrorsAndroid.OVERRIDING_WRITE_TO_PARCEL_IS_NOT_ALLOWED,
"Overriding 'writeToParcel' is not allowed. Use 'Parceler' companion object instead.")
MAP.put(ErrorsAndroid.CREATOR_DEFINITION_IS_NOT_ALLOWED,
"'CREATOR' definition is not allowed. Use 'Parceler' companion object instead.")
MAP.put(ErrorsAndroid.PARCELABLE_TYPE_NOT_SUPPORTED,
"Type is not directly supported by 'Parcelize'. " +
"Annotate the parameter type with '@RawValue' if you want it to be serialized using 'writeValue()'")
}
}