Deprecated reportFromPlugin way to report diagnostics from plugin

Originally reportFromPlugin method was introduced to address the problem
with loading of DefaultErrorMessages.Extension vis ServiceLoader.
For some cases this extension was not loaded by ServiceLoader because
classes was loaded via different class loader, common scenario here is
compiler plugins. Ideally we should load such extension point via
getService approach, but unfortunately to do that we need project and
DefaultErrorMessages.render is static method for now.
Also with reportFromPlugin approach is a problem -- all diagnostics
reported via this method has the same id: PLUGIN_[WARNING|ERROR|INFO]
and it isn't possible to suppress only one particular diagnostic.
To bypass this problem the new method
initializeFactoryNamesAndDefaultErrorMessages was introduced.
It basically store DiagnosticRenderer inside DiagnosticFactory.
It is not ideal, because one DiagnosticFactory could have different
renderers for different scenarios -- for compiler and for IDE, but
I think that it is better than reportByPlugin approach.
This commit is contained in:
Stanislav Erokhin
2020-02-07 22:18:25 +03:00
parent 84baa0b4c2
commit 453008e488
20 changed files with 104 additions and 106 deletions
@@ -21,13 +21,11 @@ import com.intellij.psi.util.PsiTreeUtil
import kotlinx.android.parcel.IgnoredOnParcel
import kotlinx.android.parcel.TypeParceler
import kotlinx.android.parcel.WriteWith
import org.jetbrains.kotlin.android.synthetic.diagnostic.DefaultErrorMessagesAndroid
import org.jetbrains.kotlin.android.synthetic.diagnostic.ErrorsAndroid
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.diagnostics.reportFromPlugin
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
@@ -70,12 +68,11 @@ class ParcelableAnnotationChecker : CallChecker {
private fun checkIgnoredOnParcelUsage(annotationEntry: KtAnnotationEntry, context: CallCheckerContext, element: KtModifierListOwner) {
if (element is KtParameter && PsiTreeUtil.getParentOfType(element, KtDeclaration::class.java) is KtPrimaryConstructor) {
context.trace.reportFromPlugin(
ErrorsAndroid.INAPPLICABLE_IGNORED_ON_PARCEL_CONSTRUCTOR_PROPERTY.on(annotationEntry),
DefaultErrorMessagesAndroid
context.trace.report(
ErrorsAndroid.INAPPLICABLE_IGNORED_ON_PARCEL_CONSTRUCTOR_PROPERTY.on(annotationEntry)
)
} else if (element !is KtProperty || PsiTreeUtil.getParentOfType(element, KtDeclaration::class.java) !is KtClass) {
context.trace.reportFromPlugin(ErrorsAndroid.INAPPLICABLE_IGNORED_ON_PARCEL.on(annotationEntry), DefaultErrorMessagesAndroid)
context.trace.report(ErrorsAndroid.INAPPLICABLE_IGNORED_ON_PARCEL.on(annotationEntry))
}
}
@@ -95,7 +92,7 @@ class ParcelableAnnotationChecker : CallChecker {
if (duplicatingAnnotationCount > 1) {
val reportElement = annotationEntry.typeArguments.firstOrNull() ?: annotationEntry
context.trace.reportFromPlugin(ErrorsAndroid.DUPLICATING_TYPE_PARCELERS.on(reportElement), DefaultErrorMessagesAndroid)
context.trace.report(ErrorsAndroid.DUPLICATING_TYPE_PARCELERS.on(reportElement))
return
}
@@ -115,8 +112,9 @@ class ParcelableAnnotationChecker : CallChecker {
// We can ignore value arguments here cause @TypeParceler is a zero-parameter annotation
if (containingClassDescriptor.annotations.any { it.type == thisAnnotationDescriptor.type }) {
val reportElement = (annotationEntry.typeReference?.typeElement as? KtUserType)?.referenceExpression ?: annotationEntry
context.trace.reportFromPlugin(
ErrorsAndroid.REDUNDANT_TYPE_PARCELER.on(reportElement, containingClass), DefaultErrorMessagesAndroid)
context.trace.report(
ErrorsAndroid.REDUNDANT_TYPE_PARCELER.on(reportElement, containingClass)
)
}
}
}
@@ -141,7 +139,7 @@ class ParcelableAnnotationChecker : CallChecker {
fun reportElement() = annotationEntry.typeArguments.singleOrNull() ?: annotationEntry
if (parcelerClass.kind != ClassKind.OBJECT) {
context.trace.reportFromPlugin(ErrorsAndroid.PARCELER_SHOULD_BE_OBJECT.on(reportElement()), DefaultErrorMessagesAndroid)
context.trace.report(ErrorsAndroid.PARCELER_SHOULD_BE_OBJECT.on(reportElement()))
return
}
@@ -150,8 +148,7 @@ class ParcelableAnnotationChecker : CallChecker {
val expectedType = parcelerSuperType.arguments.singleOrNull()?.type ?: return
if (!actualType.isSubtypeOf(expectedType)) {
context.trace.reportFromPlugin(ErrorsAndroid.PARCELER_TYPE_INCOMPATIBLE.on(reportElement(), expectedType, actualType),
DefaultErrorMessagesAndroid)
context.trace.report(ErrorsAndroid.PARCELER_TYPE_INCOMPATIBLE.on(reportElement(), expectedType, actualType))
}
}
@@ -164,8 +161,7 @@ class ParcelableAnnotationChecker : CallChecker {
val containingClassDescriptor = context.trace[BindingContext.CLASS, containingClass]
if (containingClassDescriptor != null && !containingClassDescriptor.isParcelize) {
val reportElement = (annotationEntry.typeReference?.typeElement as? KtUserType)?.referenceExpression ?: annotationEntry
context.trace.reportFromPlugin(ErrorsAndroid.CLASS_SHOULD_BE_PARCELIZE.on(reportElement, containingClass),
DefaultErrorMessagesAndroid)
context.trace.report(ErrorsAndroid.CLASS_SHOULD_BE_PARCELIZE.on(reportElement, containingClass))
}
}
}
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.android.parcel
import kotlinx.android.parcel.IgnoredOnParcel
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.FrameMap
@@ -20,7 +19,6 @@ import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.diagnostics.reportFromPlugin
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
@@ -76,9 +74,8 @@ class ParcelableDeclarationChecker : DeclarationChecker {
if (method.isWriteToParcel() && declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) {
val reportElement =
declaration.modifierList?.getModifier(KtTokens.OVERRIDE_KEYWORD) ?: declaration.nameIdentifier ?: declaration
diagnosticHolder.reportFromPlugin(
ErrorsAndroid.OVERRIDING_WRITE_TO_PARCEL_IS_NOT_ALLOWED.on(reportElement),
DefaultErrorMessagesAndroid
diagnosticHolder.report(
ErrorsAndroid.OVERRIDING_WRITE_TO_PARCEL_IS_NOT_ALLOWED.on(reportElement)
)
}
}
@@ -101,7 +98,7 @@ class ParcelableDeclarationChecker : DeclarationChecker {
&& !hasIgnoredOnParcel()
) {
val reportElement = declaration.nameIdentifier ?: declaration
diagnosticHolder.reportFromPlugin(ErrorsAndroid.PROPERTY_WONT_BE_SERIALIZED.on(reportElement), DefaultErrorMessagesAndroid)
diagnosticHolder.report(ErrorsAndroid.PROPERTY_WONT_BE_SERIALIZED.on(reportElement))
}
// @JvmName is not applicable to property so we can check just the descriptor name
@@ -109,9 +106,8 @@ class ParcelableDeclarationChecker : DeclarationChecker {
val outerClass = containingClass.containingDeclaration as? ClassDescriptor
if (outerClass != null && outerClass.isParcelize) {
val reportElement = declaration.nameIdentifier ?: declaration
diagnosticHolder.reportFromPlugin(
ErrorsAndroid.CREATOR_DEFINITION_IS_NOT_ALLOWED.on(reportElement),
DefaultErrorMessagesAndroid
diagnosticHolder.report(
ErrorsAndroid.CREATOR_DEFINITION_IS_NOT_ALLOWED.on(reportElement)
)
}
}
@@ -127,22 +123,21 @@ class ParcelableDeclarationChecker : DeclarationChecker {
if (!descriptor.isParcelize) return
if (declaration !is KtClassOrObject) {
diagnosticHolder.reportFromPlugin(ErrorsAndroid.PARCELABLE_SHOULD_BE_CLASS.on(declaration), DefaultErrorMessagesAndroid)
diagnosticHolder.report(ErrorsAndroid.PARCELABLE_SHOULD_BE_CLASS.on(declaration))
return
}
if (declaration is KtClass && (declaration.isAnnotation() || declaration.isInterface())) {
val reportElement = declaration.nameIdentifier ?: declaration
diagnosticHolder.reportFromPlugin(ErrorsAndroid.PARCELABLE_SHOULD_BE_CLASS.on(reportElement), DefaultErrorMessagesAndroid)
diagnosticHolder.report(ErrorsAndroid.PARCELABLE_SHOULD_BE_CLASS.on(reportElement))
return
}
for (companion in declaration.companionObjects) {
if (companion.name == "CREATOR") {
val reportElement = companion.nameIdentifier ?: companion
diagnosticHolder.reportFromPlugin(
ErrorsAndroid.CREATOR_DEFINITION_IS_NOT_ALLOWED.on(reportElement),
DefaultErrorMessagesAndroid
diagnosticHolder.report(
ErrorsAndroid.CREATOR_DEFINITION_IS_NOT_ALLOWED.on(reportElement)
)
}
}
@@ -150,26 +145,25 @@ class ParcelableDeclarationChecker : DeclarationChecker {
val sealedOrAbstract =
declaration.modifierList?.let { it.getModifier(KtTokens.ABSTRACT_KEYWORD) ?: it.getModifier(KtTokens.SEALED_KEYWORD) }
if (sealedOrAbstract != null) {
diagnosticHolder.reportFromPlugin(
ErrorsAndroid.PARCELABLE_SHOULD_BE_INSTANTIABLE.on(sealedOrAbstract),
DefaultErrorMessagesAndroid
diagnosticHolder.report(
ErrorsAndroid.PARCELABLE_SHOULD_BE_INSTANTIABLE.on(sealedOrAbstract)
)
}
if (declaration is KtClass && declaration.isInner()) {
val reportElement = declaration.modifierList?.getModifier(KtTokens.INNER_KEYWORD) ?: declaration.nameIdentifier ?: declaration
diagnosticHolder.reportFromPlugin(ErrorsAndroid.PARCELABLE_CANT_BE_INNER_CLASS.on(reportElement), DefaultErrorMessagesAndroid)
diagnosticHolder.report(ErrorsAndroid.PARCELABLE_CANT_BE_INNER_CLASS.on(reportElement))
}
if (declaration.isLocal) {
val reportElement = declaration.nameIdentifier ?: declaration
diagnosticHolder.reportFromPlugin(ErrorsAndroid.PARCELABLE_CANT_BE_LOCAL_CLASS.on(reportElement), DefaultErrorMessagesAndroid)
diagnosticHolder.report(ErrorsAndroid.PARCELABLE_CANT_BE_LOCAL_CLASS.on(reportElement))
}
val superTypes = TypeUtils.getAllSupertypes(descriptor.defaultType)
if (superTypes.none { it.constructor.declarationDescriptor?.fqNameSafe == ANDROID_PARCELABLE_CLASS_FQNAME }) {
val reportElement = declaration.nameIdentifier ?: declaration
diagnosticHolder.reportFromPlugin(ErrorsAndroid.NO_PARCELABLE_SUPERTYPE.on(reportElement), DefaultErrorMessagesAndroid)
diagnosticHolder.report(ErrorsAndroid.NO_PARCELABLE_SUPERTYPE.on(reportElement))
}
for (supertypeEntry in declaration.superTypeListEntries) {
@@ -178,9 +172,8 @@ class ParcelableDeclarationChecker : DeclarationChecker {
val type = bindingContext[BindingContext.TYPE, supertypeEntry.typeReference] ?: continue
if (type.isParcelable()) {
val reportElement = supertypeEntry.byKeywordNode?.psi ?: delegateExpression
diagnosticHolder.reportFromPlugin(
ErrorsAndroid.PARCELABLE_DELEGATE_IS_NOT_ALLOWED.on(reportElement),
DefaultErrorMessagesAndroid
diagnosticHolder.report(
ErrorsAndroid.PARCELABLE_DELEGATE_IS_NOT_ALLOWED.on(reportElement)
)
}
}
@@ -188,15 +181,13 @@ class ParcelableDeclarationChecker : DeclarationChecker {
val primaryConstructor = declaration.primaryConstructor
if (primaryConstructor == null && declaration.secondaryConstructors.isNotEmpty()) {
val reportElement = declaration.nameIdentifier ?: declaration
diagnosticHolder.reportFromPlugin(
ErrorsAndroid.PARCELABLE_SHOULD_HAVE_PRIMARY_CONSTRUCTOR.on(reportElement),
DefaultErrorMessagesAndroid
diagnosticHolder.report(
ErrorsAndroid.PARCELABLE_SHOULD_HAVE_PRIMARY_CONSTRUCTOR.on(reportElement)
)
} else if (primaryConstructor != null && primaryConstructor.valueParameters.isEmpty()) {
val reportElement = declaration.nameIdentifier ?: declaration
diagnosticHolder.reportFromPlugin(
ErrorsAndroid.PARCELABLE_PRIMARY_CONSTRUCTOR_IS_EMPTY.on(reportElement),
DefaultErrorMessagesAndroid
diagnosticHolder.report(
ErrorsAndroid.PARCELABLE_PRIMARY_CONSTRUCTOR_IS_EMPTY.on(reportElement)
)
}
@@ -220,8 +211,8 @@ class ParcelableDeclarationChecker : DeclarationChecker {
) {
if (!parameter.hasValOrVar()) {
val reportElement = parameter.nameIdentifier ?: parameter
diagnosticHolder.reportFromPlugin(
ErrorsAndroid.PARCELABLE_CONSTRUCTOR_PARAMETER_SHOULD_BE_VAL_OR_VAR.on(reportElement), DefaultErrorMessagesAndroid
diagnosticHolder.report(
ErrorsAndroid.PARCELABLE_CONSTRUCTOR_PARAMETER_SHOULD_BE_VAL_OR_VAR.on(reportElement)
)
}
@@ -244,9 +235,8 @@ class ParcelableDeclarationChecker : DeclarationChecker {
} catch (e: IllegalArgumentException) {
// get() throws IllegalArgumentException on unknown types
val reportElement = parameter.typeReference ?: parameter.nameIdentifier ?: parameter
diagnosticHolder.reportFromPlugin(
ErrorsAndroid.PARCELABLE_TYPE_NOT_SUPPORTED.on(reportElement),
DefaultErrorMessagesAndroid
diagnosticHolder.report(
ErrorsAndroid.PARCELABLE_TYPE_NOT_SUPPORTED.on(reportElement)
)
}
}
@@ -23,7 +23,6 @@ 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
@@ -51,7 +50,7 @@ class AndroidExtensionPropertiesCallChecker : CallChecker {
private fun DiagnosticSink.checkDeprecated(expression: KtExpression, packageDescriptor: AndroidSyntheticPackageFragmentDescriptor) {
if (packageDescriptor.packageData.isDeprecated) {
reportFromPlugin(SYNTHETIC_DEPRECATED_PACKAGE.on(expression), DefaultErrorMessagesAndroid)
report(SYNTHETIC_DEPRECATED_PACKAGE.on(expression))
}
}
@@ -60,7 +59,7 @@ class AndroidExtensionPropertiesCallChecker : CallChecker {
val type = property.errorType ?: return
val warning = if (type.contains('.')) SYNTHETIC_UNRESOLVED_WIDGET_TYPE else SYNTHETIC_INVALID_WIDGET_TYPE
reportFromPlugin(warning.on(expression, type), DefaultErrorMessagesAndroid)
report(warning.on(expression, type))
}
private fun DiagnosticSink.checkPartiallyDefinedResource(
@@ -73,7 +72,7 @@ class AndroidExtensionPropertiesCallChecker : CallChecker {
val expectedType = context.resolutionContext.expectedType
if (!TypeUtils.noExpectedType(expectedType) && !expectedType.isMarkedNullable && !expectedType.isFlexible()) {
reportFromPlugin(UNSAFE_CALL_ON_PARTIALLY_DEFINED_RESOURCE.on(calleeExpression), DefaultErrorMessagesAndroid)
report(UNSAFE_CALL_ON_PARTIALLY_DEFINED_RESOURCE.on(calleeExpression))
return
}
@@ -81,7 +80,7 @@ class AndroidExtensionPropertiesCallChecker : CallChecker {
val usage = outermostQualifiedExpression.parent
if (usage is KtDotQualifiedExpression && usage.receiverExpression == outermostQualifiedExpression) {
reportFromPlugin(UNSAFE_CALL_ON_PARTIALLY_DEFINED_RESOURCE.on(calleeExpression), DefaultErrorMessagesAndroid)
report(UNSAFE_CALL_ON_PARTIALLY_DEFINED_RESOURCE.on(calleeExpression))
}
}
@@ -21,9 +21,7 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0;
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1;
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory2;
import org.jetbrains.kotlin.diagnostics.Errors;
import org.jetbrains.kotlin.psi.KtAnnotationEntry;
import org.jetbrains.kotlin.psi.KtClassOrObject;
import org.jetbrains.kotlin.psi.KtElement;
import org.jetbrains.kotlin.psi.KtExpression;
import org.jetbrains.kotlin.types.KotlinType;
@@ -62,7 +60,7 @@ public interface ErrorsAndroid {
@SuppressWarnings("UnusedDeclaration")
Object _initializer = new Object() {
{
Errors.Initializer.initializeFactoryNames(ErrorsAndroid.class);
Errors.Initializer.initializeFactoryNamesAndDefaultErrorMessages(ErrorsAndroid.class, DefaultErrorMessagesAndroid.INSTANCE);
}
};