diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index b0e75570b62..7bd6b5df150 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -271,12 +271,12 @@ public class DefaultErrorMessages { 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 = Bar'"); MAP.put(HEADER_WITHOUT_IMPLEMENTATION, "''header'' {0} has no implementation in module{1}{2}", DECLARATION_NAME_WITH_KIND, - PLATFORM, PlatformIncompatibilityDiagnosticRenderer.INSTANCE); + PLATFORM, PlatformIncompatibilityDiagnosticRenderer.TEXT); MAP.put(IMPLEMENTATION_WITHOUT_HEADER, "''impl'' {0} has no corresponding ''header'' declaration{1}", DECLARATION_NAME_WITH_KIND, - PlatformIncompatibilityDiagnosticRenderer.INSTANCE); + PlatformIncompatibilityDiagnosticRenderer.TEXT); MAP.put(HEADER_CLASS_MEMBERS_ARE_NOT_IMPLEMENTED, "''impl'' class ''{0}'' has no implementation of ''header'' class members:{1}", - NAME, IncompatibleHeaderImplClassScopesRenderer.INSTANCE); + NAME, IncompatibleHeaderImplClassScopesRenderer.TEXT); MAP.put(IMPL_MISSING, "Declaration should be marked with 'impl' (suppress with -Xno-check-impl)"); MAP.put(PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT, "Projections are not allowed on type arguments of functions and properties"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/PlatformIncompatibilityDiagnosticRenderer.kt b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/PlatformIncompatibilityDiagnosticRenderer.kt index 47d5d0d8c19..94e3baeefc4 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/PlatformIncompatibilityDiagnosticRenderer.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/PlatformIncompatibilityDiagnosticRenderer.kt @@ -21,7 +21,9 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.MemberDescriptor import org.jetbrains.kotlin.resolve.checkers.HeaderImplDeclarationChecker.Compatibility.Incompatible -object PlatformIncompatibilityDiagnosticRenderer : DiagnosticParameterRenderer>> { +class PlatformIncompatibilityDiagnosticRenderer( + private val mode: MultiplatformDiagnosticRenderingMode +) : DiagnosticParameterRenderer>> { override fun render( obj: Map>, renderingContext: RenderingContext @@ -29,47 +31,80 @@ object PlatformIncompatibilityDiagnosticRenderer : DiagnosticParameterRenderer>>>> { +class IncompatibleHeaderImplClassScopesRenderer( + private val mode: MultiplatformDiagnosticRenderingMode +) : DiagnosticParameterRenderer>>>> { override fun render( obj: List>>>, - renderingContext: RenderingContext): String { + renderingContext: RenderingContext + ): String { if (obj.isEmpty()) return "" return buildString { - appendln() - renderIncompatibleClassScopes(obj, "", renderingContext) + mode.newLine(this) + renderIncompatibleClassScopes(obj, "", renderingContext, mode) } } + + companion object { + @JvmField + val TEXT = IncompatibleHeaderImplClassScopesRenderer(MultiplatformDiagnosticRenderingMode()) + } +} + +open class MultiplatformDiagnosticRenderingMode { + open fun newLine(sb: StringBuilder) { + sb.appendln() + } + + open fun renderList(sb: StringBuilder, elements: List<() -> Unit>) { + sb.appendln() + for (element in elements) { + element() + } + } + + open fun renderDescriptor(sb: StringBuilder, descriptor: DeclarationDescriptor, context: RenderingContext, indent: String) { + sb.append(indent) + sb.append(INDENTATION_UNIT) + sb.appendln(Renderers.COMPACT_WITH_MODIFIERS.render(descriptor, context)) + } } private fun StringBuilder.renderIncompatibilityInformation( map: Map>, indent: String, - context: RenderingContext + context: RenderingContext, + mode: MultiplatformDiagnosticRenderingMode ) { for ((incompatibility, descriptors) in map) { append(indent) append("The following declaration") if (descriptors.size == 1) append(" is") else append("s are") append(" incompatible") - incompatibility.reason?.let { appendln(" because $it:") } + incompatibility.reason?.let { append(" because $it") } + append(":") - for (descriptor in descriptors) { - append(indent + " ") - appendln(descriptor.render(context)) - } + mode.renderList(this, descriptors.map { descriptor -> + { mode.renderDescriptor(this, descriptor, context, indent) } + }) if (incompatibility is Incompatible.ClassScopes) { append(indent) - appendln("No implementations are found for members listed below:") - renderIncompatibleClassScopes(incompatibility.unimplemented, indent, context) + append("No implementations are found for members listed below:") + mode.newLine(this) + renderIncompatibleClassScopes(incompatibility.unimplemented, indent, context, mode) } } } @@ -77,21 +112,22 @@ private fun StringBuilder.renderIncompatibilityInformation( private fun StringBuilder.renderIncompatibleClassScopes( unimplemented: List>>>, indent: String, - context: RenderingContext + context: RenderingContext, + mode: MultiplatformDiagnosticRenderingMode ) { - for ((descriptor, mapping) in unimplemented) { - appendln() - append(indent + " ") - appendln(descriptor.render(context)) - if (mapping.isNotEmpty()) { - appendln() + mode.renderList(this, unimplemented.indices.map { index -> + { + val (descriptor, mapping) = unimplemented[index] + mode.renderDescriptor(this, descriptor, context, indent) + if (mapping.isNotEmpty()) { + mode.newLine(this) + renderIncompatibilityInformation(mapping, indent + INDENTATION_UNIT, context, mode) + } + if (index != unimplemented.lastIndex) { + mode.newLine(this) + } } - renderIncompatibilityInformation(mapping, indent + INDENTATION_UNIT, context) - } + }) } private const val INDENTATION_UNIT = " " - -private fun DeclarationDescriptor.render(context: RenderingContext): String { - return Renderers.COMPACT_WITH_MODIFIERS.render(this, context) -} diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/IdeErrorMessages.java b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/IdeErrorMessages.java index e6c16d5108c..fd97612b14a 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/IdeErrorMessages.java +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/IdeErrorMessages.java @@ -28,8 +28,7 @@ import org.jetbrains.kotlin.js.resolve.diagnostics.JsCallDataHtmlRenderer; import java.net.URL; import static org.jetbrains.kotlin.diagnostics.Errors.*; -import static org.jetbrains.kotlin.diagnostics.rendering.Renderers.RENDER_CLASS_OR_OBJECT; -import static org.jetbrains.kotlin.diagnostics.rendering.Renderers.STRING; +import static org.jetbrains.kotlin.diagnostics.rendering.Renderers.*; import static org.jetbrains.kotlin.diagnostics.rendering.TabledDescriptorRenderer.TextElementType; import static org.jetbrains.kotlin.idea.highlighter.HtmlTabledDescriptorRenderer.tableForTypes; import static org.jetbrains.kotlin.idea.highlighter.IdeRenderers.*; @@ -176,6 +175,14 @@ public class IdeErrorMessages { MAP.put(EXPERIMENTAL_FEATURE_WARNING, "{0}", new LanguageFeatureMessageRenderer(LanguageFeatureMessageRenderer.Type.WARNING, true)); MAP.put(EXPERIMENTAL_FEATURE_ERROR, "{0}", new LanguageFeatureMessageRenderer(LanguageFeatureMessageRenderer.Type.ERROR, true)); + MAP.put(HEADER_WITHOUT_IMPLEMENTATION, "''header'' {0} has no implementation in module{1}{2}", DECLARATION_NAME_WITH_KIND, + PLATFORM, new PlatformIncompatibilityDiagnosticRenderer(IdeMultiplatformDiagnosticRenderingMode.INSTANCE)); + MAP.put(IMPLEMENTATION_WITHOUT_HEADER, "''impl'' {0} has no corresponding ''header'' declaration{1}", DECLARATION_NAME_WITH_KIND, + new PlatformIncompatibilityDiagnosticRenderer(IdeMultiplatformDiagnosticRenderingMode.INSTANCE)); + + MAP.put(HEADER_CLASS_MEMBERS_ARE_NOT_IMPLEMENTED, "''impl'' class ''{0}'' has no implementation of ''header'' class members:{1}", + NAME, new IncompatibleHeaderImplClassScopesRenderer(IdeMultiplatformDiagnosticRenderingMode.INSTANCE)); + MAP.setImmutable(); } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/IdeMultiplatformDiagnosticRenderingMode.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/IdeMultiplatformDiagnosticRenderingMode.kt new file mode 100644 index 00000000000..6eca64b89b5 --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/IdeMultiplatformDiagnosticRenderingMode.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2010-2017 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.idea.highlighter + +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.diagnostics.rendering.MultiplatformDiagnosticRenderingMode +import org.jetbrains.kotlin.diagnostics.rendering.RenderingContext + +object IdeMultiplatformDiagnosticRenderingMode : MultiplatformDiagnosticRenderingMode() { + override fun newLine(sb: StringBuilder) { + sb.append("
") + } + + override fun renderList(sb: StringBuilder, elements: List<() -> Unit>) { + sb.append("
    ") + for (element in elements) { + sb.append("
  • ") + element() + sb.append("
  • ") + } + sb.append("
") + } + + override fun renderDescriptor(sb: StringBuilder, descriptor: DeclarationDescriptor, context: RenderingContext, indent: String) { + sb.append(IdeRenderers.HTML.render(descriptor, context)) + } +}