Improve header/impl mismatch diagnostic messages
Try to report most mismatch errors on the 'impl' declaration. Only report a mismatch error on the 'header' declaration if no error would be otherwise reported on any 'impl' declaration in the compilation unit. Also render declaration kind in the message #KT-18447 In Progress
This commit is contained in:
@@ -562,7 +562,9 @@ public interface Errors {
|
||||
DiagnosticFactory3<KtDeclaration, MemberDescriptor, ModuleDescriptor,
|
||||
Map<HeaderImplDeclarationChecker.Compatibility.Incompatible, Collection<MemberDescriptor>>> HEADER_WITHOUT_IMPLEMENTATION =
|
||||
DiagnosticFactory3.create(ERROR, DECLARATION_SIGNATURE);
|
||||
DiagnosticFactory0<PsiElement> IMPLEMENTATION_WITHOUT_HEADER = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory2<KtDeclaration, MemberDescriptor,
|
||||
Map<HeaderImplDeclarationChecker.Compatibility.Incompatible, Collection<MemberDescriptor>>> IMPLEMENTATION_WITHOUT_HEADER =
|
||||
DiagnosticFactory2.create(ERROR, DECLARATION_SIGNATURE);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
+3
-2
@@ -270,9 +270,10 @@ public class DefaultErrorMessages {
|
||||
MAP.put(IMPL_TYPE_ALIAS_WITH_USE_SITE_VARIANCE, "Right-hand side of 'impl' type alias cannot contain use-site variance or star projections");
|
||||
MAP.put(IMPL_TYPE_ALIAS_WITH_COMPLEX_SUBSTITUTION, "Type arguments in the right-hand side of 'impl' type alias should be its type parameters in the same order, e.g. 'impl typealias Foo<A, B> = Bar<A, B>'");
|
||||
|
||||
MAP.put(HEADER_WITHOUT_IMPLEMENTATION, "Header declaration ''{0}'' has no implementation in module{1}{2}", NAME,
|
||||
MAP.put(HEADER_WITHOUT_IMPLEMENTATION, "''header'' {0} has no implementation in module{1}{2}", DECLARATION_NAME_WITH_KIND,
|
||||
PLATFORM, PlatformIncompatibilityDiagnosticRenderer.INSTANCE);
|
||||
MAP.put(IMPLEMENTATION_WITHOUT_HEADER, "Modifier 'impl' is only applicable to members that are initially declared in platform-independent code");
|
||||
MAP.put(IMPLEMENTATION_WITHOUT_HEADER, "''impl'' {0} has no corresponding ''header'' declaration{1}", DECLARATION_NAME_WITH_KIND,
|
||||
PlatformIncompatibilityDiagnosticRenderer.INSTANCE);
|
||||
|
||||
MAP.put(PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT, "Projections are not allowed on type arguments of functions and properties");
|
||||
MAP.put(SUPERTYPE_NOT_INITIALIZED, "This type has a constructor, and thus must be initialized here");
|
||||
|
||||
@@ -93,19 +93,19 @@ object Renderers {
|
||||
}
|
||||
|
||||
@JvmField val DECLARATION_NAME_WITH_KIND = Renderer<DeclarationDescriptor> {
|
||||
val declarationKindWithSpace = when (it) {
|
||||
is PackageFragmentDescriptor -> "package "
|
||||
is ClassDescriptor -> "${it.renderKind()} "
|
||||
is TypeAliasDescriptor -> "typealias "
|
||||
is ConstructorDescriptor -> "constructor "
|
||||
is TypeAliasConstructorDescriptor -> "typealias constructor "
|
||||
is PropertyGetterDescriptor -> "property getter "
|
||||
is PropertySetterDescriptor -> "property setter "
|
||||
is FunctionDescriptor -> "function "
|
||||
is PropertyDescriptor -> "property "
|
||||
val name = it.name.asString()
|
||||
when (it) {
|
||||
is PackageFragmentDescriptor -> "package '$name'"
|
||||
is ClassDescriptor -> "${it.renderKind()} '$name'"
|
||||
is TypeAliasDescriptor -> "typealias '$name'"
|
||||
is TypeAliasConstructorDescriptor -> "constructor of '${it.typeAliasDescriptor.name.asString()}'"
|
||||
is ConstructorDescriptor -> "constructor of '${it.constructedClass.name.asString()}'"
|
||||
is PropertyGetterDescriptor -> "getter of property '${it.correspondingProperty.name.asString()}'"
|
||||
is PropertySetterDescriptor -> "setter of property '${it.correspondingProperty.name.asString()}'"
|
||||
is FunctionDescriptor -> "function '$name'"
|
||||
is PropertyDescriptor -> "property '$name'"
|
||||
else -> throw AssertionError("Unexpected declaration kind: $it")
|
||||
}
|
||||
"$declarationKindWithSpace'${it.name.asString()}'"
|
||||
}
|
||||
|
||||
@JvmField val NAME_OF_CONTAINING_DECLARATION_OR_FILE = Renderer<DeclarationDescriptor> {
|
||||
|
||||
+14
-6
@@ -77,9 +77,16 @@ object HeaderImplDeclarationChecker : DeclarationChecker {
|
||||
platformModule: ModuleDescriptor,
|
||||
checkImpl: Boolean
|
||||
) {
|
||||
val compatibility = findImplForHeader(descriptor, platformModule, checkImpl)
|
||||
val compatibility = findImplForHeader(descriptor, platformModule, checkImpl) ?: return
|
||||
|
||||
if (compatibility != null && Compatible !in compatibility) {
|
||||
val shouldReportError =
|
||||
compatibility.isEmpty() ||
|
||||
Compatible !in compatibility && compatibility.values.flatMapTo(hashSetOf()) { it }.all { impl ->
|
||||
val headers = findHeaderForImpl(impl, descriptor.module)
|
||||
headers != null && Compatible in headers.keys
|
||||
}
|
||||
|
||||
if (shouldReportError) {
|
||||
assert(compatibility.keys.all { it is Incompatible })
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val incompatibility = compatibility as Map<Incompatible, Collection<MemberDescriptor>>
|
||||
@@ -120,12 +127,13 @@ object HeaderImplDeclarationChecker : DeclarationChecker {
|
||||
// Using the platform module instead of the common module is sort of fine here because the former always depends on the latter.
|
||||
// However, it would be clearer to find the common module this platform module implements and look for headers there instead.
|
||||
// TODO: use common module here
|
||||
val compatibility = findHeaderForImpl(descriptor, descriptor.module)
|
||||
val compatibility = findHeaderForImpl(descriptor, descriptor.module) ?: return
|
||||
|
||||
if (compatibility != null && Compatible !in compatibility) {
|
||||
if (Compatible !in compatibility) {
|
||||
assert(compatibility.keys.all { it is Incompatible })
|
||||
// TODO: do not report this error for members which are "almost compatible" with some header declarations
|
||||
diagnosticHolder.report(Errors.IMPLEMENTATION_WITHOUT_HEADER.on(reportOn.modifierList!!.getModifier(KtTokens.IMPL_KEYWORD)!!))
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val incompatibility = compatibility as Map<Incompatible, Collection<MemberDescriptor>>
|
||||
diagnosticHolder.report(Errors.IMPLEMENTATION_WITHOUT_HEADER.on(reportOn, descriptor, incompatibility))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user