From 399b7aceefbc201bd9b31c147fbdd7b7514b2ec8 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Tue, 8 Jun 2021 19:17:46 +0300 Subject: [PATCH] [FIR] Fix reporting diagnostics in CLI which was broken in previous commit Note: reporting properly works only for PSI --- .../FirDiagnosticsCompilerResultsReporter.kt | 88 +++++++++++++++++++ .../FirKotlinToJvmBytecodeCompiler.kt | 14 +-- .../diagnostics/FirDefaultErrorMessages.kt | 5 +- .../kotlin/diagnostics/DiagnosticUtils.java | 7 +- .../fir/handlers/FirDiagnosticCodeMetaInfo.kt | 3 +- 5 files changed, 101 insertions(+), 16 deletions(-) create mode 100644 compiler/cli/src/org/jetbrains/kotlin/cli/common/fir/FirDiagnosticsCompilerResultsReporter.kt diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/common/fir/FirDiagnosticsCompilerResultsReporter.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/common/fir/FirDiagnosticsCompilerResultsReporter.kt new file mode 100644 index 00000000000..b6e4328b465 --- /dev/null +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/common/fir/FirDiagnosticsCompilerResultsReporter.kt @@ -0,0 +1,88 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.cli.common.fir + +import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSourceLocation +import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.cli.common.messages.MessageUtil +import org.jetbrains.kotlin.diagnostics.DiagnosticUtils +import org.jetbrains.kotlin.diagnostics.Severity +import org.jetbrains.kotlin.fir.FirLightSourceElement +import org.jetbrains.kotlin.fir.FirPsiSourceElement +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDefaultErrorMessages +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnostic + +object FirDiagnosticsCompilerResultsReporter { + fun reportDiagnostics(diagnostics: Collection>, reporter: MessageCollector): Boolean { + var hasErrors = false + for (diagnostic in diagnostics.sortedWith(DiagnosticComparator)) { + hasErrors = reportDiagnostic(diagnostic, reporter) || hasErrors + } + reportSpecialErrors(diagnostics) + return hasErrors + } + + @Suppress("UNUSED_PARAMETER") + private fun reportSpecialErrors(diagnostics: Collection>) { + /* + * TODO: handle next diagnostics when they will be supported in FIR: + * - INCOMPATIBLE_CLASS + * - PRE_RELEASE_CLASS + * - IR_WITH_UNSTABLE_ABI_COMPILED_CLASS + * - FIR_COMPILED_CLASS + */ + } + + private fun reportDiagnostic(diagnostic: FirDiagnostic<*>, reporter: MessageCollector): Boolean { + if (!diagnostic.isValid) return false + diagnostic.location()?.let { location -> + val severity = AnalyzerWithCompilerReport.convertSeverity(diagnostic.severity) + // TODO: support multiple maps with messages + val renderer = FirDefaultErrorMessages.getRendererForDiagnostic(diagnostic) + reporter.report(severity, renderer.render(diagnostic), location) + } + return diagnostic.severity == Severity.ERROR + } + + private fun FirDiagnostic<*>.location(): CompilerMessageSourceLocation? = when (val element = element) { + is FirPsiSourceElement<*> -> element.location(this) + is FirLightSourceElement -> element.location(this) + } + + private fun FirPsiSourceElement<*>.location(diagnostic: FirDiagnostic<*>): CompilerMessageSourceLocation? { + val file = psi.containingFile + return MessageUtil.psiFileToMessageLocation(file, file.name, DiagnosticUtils.getLineAndColumnRange(file, diagnostic.textRanges)) + } + + @Suppress("UNUSED_PARAMETER") + private fun FirLightSourceElement.location(diagnostic: FirDiagnostic<*>): CompilerMessageSourceLocation? { + // TODO: support light tree + return null + } + + private object DiagnosticComparator : Comparator> { + override fun compare(o1: FirDiagnostic<*>, o2: FirDiagnostic<*>): Int { + val element1 = o1.element + val element2 = o1.element + // TODO: support light tree + if (element1 !is FirPsiSourceElement<*> || element2 !is FirPsiSourceElement<*>) return 0 + + val file1 = element1.psi.containingFile + val file2 = element2.psi.containingFile + val path1 = file1.viewProvider.virtualFile.path + val path2 = file2.viewProvider.virtualFile.path + if (path1 != path2) return path1.compareTo(path2) + + val range1 = DiagnosticUtils.firstRange(o1.textRanges) + val range2 = DiagnosticUtils.firstRange(o2.textRanges) + + return if (range1 != range2) { + DiagnosticUtils.TEXT_RANGE_COMPARATOR.compare(range1, range2) + } else o1.factory.name.compareTo(o2.factory.name) + } + } +} diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/FirKotlinToJvmBytecodeCompiler.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/FirKotlinToJvmBytecodeCompiler.kt index cc77a428f4c..086557f7072 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/FirKotlinToJvmBytecodeCompiler.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/FirKotlinToJvmBytecodeCompiler.kt @@ -19,10 +19,9 @@ import org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory import org.jetbrains.kotlin.backend.jvm.jvmPhases import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys import org.jetbrains.kotlin.cli.common.checkKotlinPackageUsage +import org.jetbrains.kotlin.cli.common.fir.FirDiagnosticsCompilerResultsReporter import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.STRONG_WARNING -import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot -import org.jetbrains.kotlin.cli.jvm.config.JvmModulePathRoot import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots import org.jetbrains.kotlin.cli.jvm.config.jvmModularRoots import org.jetbrains.kotlin.codegen.ClassBuilderFactories @@ -32,14 +31,12 @@ import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.JVMConfigurationKeys import org.jetbrains.kotlin.config.languageVersionSettings import org.jetbrains.kotlin.container.get -import org.jetbrains.kotlin.diagnostics.Severity import org.jetbrains.kotlin.fir.analysis.FirAnalyzerFacade import org.jetbrains.kotlin.fir.backend.jvm.FirJvmBackendClassResolver import org.jetbrains.kotlin.fir.backend.jvm.FirJvmBackendExtension import org.jetbrains.kotlin.fir.checkers.registerExtendedCommonCheckers import org.jetbrains.kotlin.fir.session.FirSessionFactory import org.jetbrains.kotlin.fir.session.FirSessionFactory.createSessionWithDependencies -import org.jetbrains.kotlin.load.kotlin.PackagePartProvider import org.jetbrains.kotlin.load.kotlin.incremental.IncrementalPackagePartProvider import org.jetbrains.kotlin.modules.Module import org.jetbrains.kotlin.modules.TargetId @@ -48,7 +45,6 @@ import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.platform.jvm.JvmPlatforms import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus import org.jetbrains.kotlin.resolve.CompilerEnvironment -import org.jetbrains.kotlin.resolve.diagnostics.SimpleGenericDiagnostics import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory import org.jetbrains.kotlin.utils.newLinkedHashMapWithExpectedSize @@ -147,14 +143,10 @@ object FirKotlinToJvmBytecodeCompiler { firAnalyzerFacade.runResolution() val firDiagnostics = firAnalyzerFacade.runCheckers().values.flatten() - // TODO -// AnalyzerWithCompilerReport.reportDiagnostics( -// SimpleGenericDiagnostics(firDiagnostics), -// environment.messageCollector -// ) + val hasErrors = FirDiagnosticsCompilerResultsReporter.reportDiagnostics(firDiagnostics, environment.messageCollector) performanceManager?.notifyAnalysisFinished() - if (syntaxErrors || firDiagnostics.any { it.severity == Severity.ERROR }) { + if (syntaxErrors || hasErrors) { return false } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt index 0dd188a5529..585f2ad054f 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt @@ -379,9 +379,10 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_SETTER_RETU @Suppress("unused") class FirDefaultErrorMessages { companion object { - fun getRendererForDiagnostic(diagnostic: FirDiagnostic<*>): FirDiagnosticRenderer<*> { + fun getRendererForDiagnostic(diagnostic: FirDiagnostic<*>): FirDiagnosticRenderer> { val factory = diagnostic.factory - return MAP[factory] ?: factory.firRenderer + @Suppress("UNCHECKED_CAST") + return (MAP[factory] ?: factory.firRenderer) as FirDiagnosticRenderer> } // * - The old FE reports these diagnostics with additional parameters diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/DiagnosticUtils.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/DiagnosticUtils.java index fcbb7dd2eb4..f2d2c7cd431 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/DiagnosticUtils.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/DiagnosticUtils.java @@ -36,7 +36,7 @@ import java.util.List; public class DiagnosticUtils { @NotNull - private static final Comparator TEXT_RANGE_COMPARATOR = (o1, o2) -> { + public static final Comparator TEXT_RANGE_COMPARATOR = (o1, o2) -> { if (o1.getStartOffset() != o2.getStartOffset()) { return o1.getStartOffset() - o2.getStartOffset(); } @@ -87,6 +87,11 @@ public class DiagnosticUtils { public static PsiDiagnosticUtils.LineAndColumnRange getLineAndColumnRange(@NotNull Diagnostic diagnostic) { PsiFile file = diagnostic.getPsiFile(); List textRanges = diagnostic.getTextRanges(); + return getLineAndColumnRange(file, textRanges); + } + + @NotNull + public static PsiDiagnosticUtils.LineAndColumnRange getLineAndColumnRange(PsiFile file, List textRanges) { if (textRanges.isEmpty()) return PsiDiagnosticUtils.LineAndColumnRange.NONE; TextRange firstRange = firstRange(textRanges); return getLineAndColumnRangeInPsiFile(file, firstRange); diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticCodeMetaInfo.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticCodeMetaInfo.kt index f5d9680ea6e..a36c37346d7 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticCodeMetaInfo.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticCodeMetaInfo.kt @@ -11,7 +11,6 @@ import org.jetbrains.kotlin.codeMetaInfo.renderConfigurations.AbstractCodeMetaIn import org.jetbrains.kotlin.fir.analysis.diagnostics.AbstractFirDiagnosticWithParametersRenderer import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDefaultErrorMessages import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnostic -import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderer object FirMetaInfoUtils { val renderDiagnosticNoArgs = FirDiagnosticCodeMetaRenderConfiguration().apply { renderParams = false } @@ -67,7 +66,7 @@ class FirDiagnosticCodeMetaRenderConfiguration( val diagnostic = codeMetaInfo.diagnostic @Suppress("UNCHECKED_CAST") - val renderer = FirDefaultErrorMessages.getRendererForDiagnostic(diagnostic) as FirDiagnosticRenderer> + val renderer = FirDefaultErrorMessages.getRendererForDiagnostic(diagnostic) if (renderer is AbstractFirDiagnosticWithParametersRenderer<*>) { renderer.renderParameters(diagnostic).mapTo(params, Any?::toString) }