Skip to content

Commit

Permalink
[FIR] Report SYNTAX errors from PSI parser to diagnostic collector
Browse files Browse the repository at this point in the history
Previously they were reported directly to message collector
  • Loading branch information
demiurg906 authored and Space Cloud committed Jan 22, 2025
1 parent 47e6f20 commit 2b0897c
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,17 @@ package org.jetbrains.kotlin.cli.common.messages
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.*
import com.intellij.psi.util.PsiFormatUtil
import org.jetbrains.kotlin.KtRealPsiSourceElement
import org.jetbrains.kotlin.analyzer.AbstractAnalyzerWithCompilerReport
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.*
import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.diagnostics.*
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils.sortedDiagnostics
import org.jetbrains.kotlin.diagnostics.impl.BaseDiagnosticsCollector
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
import org.jetbrains.kotlin.fir.builder.FirSyntaxErrors
import org.jetbrains.kotlin.load.java.components.TraceBasedErrorReporter
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.*
Expand Down Expand Up @@ -218,19 +221,45 @@ class AnalyzerWithCompilerReport(
}
}

// Reports K1 diagnostics ([org.jetbrains.kotlin.diagnostics.SimpleDiagnostic])
fun reportSyntaxErrors(file: PsiElement, reporter: DiagnosticMessageReporter): SyntaxErrorReport {
class ErrorReportingVisitor : AnalyzingUtils.PsiErrorElementVisitor() {
var hasErrors = false
var allErrorsAtEof = true
return reportSyntaxErrors(file) { element, message ->
val diagnostic = MyDiagnostic(element, SYNTAX_ERROR_FACTORY, message)
AnalyzerWithCompilerReport.reportDiagnostic(diagnostic, reporter, renderDiagnosticName = false)
}
}

private fun <E : PsiElement> reportDiagnostic(element: E, factory: DiagnosticFactory0<E>, message: String) {
val diagnostic = MyDiagnostic(element, factory, message)
AnalyzerWithCompilerReport.reportDiagnostic(diagnostic, reporter, renderDiagnosticName = false)
if (allErrorsAtEof && !element.isAtEof()) {
allErrorsAtEof = false
// Reports K2 diagnostics ([org.jetbrains.kotlin.diagnostics.KtDiagnostic])
fun reportSyntaxErrors(file: PsiElement, diagnosticCollector: BaseDiagnosticsCollector): SyntaxErrorReport {
return reportSyntaxErrors(file) { element, message ->
@OptIn(InternalDiagnosticFactoryMethod::class)
val diagnostic = FirSyntaxErrors.SYNTAX.on(
KtRealPsiSourceElement(element),
message,
positioningStrategy = null
)
val context = object : DiagnosticContext {
override val containingFilePath: String?
get() = file.containingFile.virtualFile?.path

override fun isDiagnosticSuppressed(diagnostic: KtDiagnostic): Boolean {
return false
}
hasErrors = true

override val languageVersionSettings: LanguageVersionSettings
get() = LanguageVersionSettingsImpl.DEFAULT
}
diagnosticCollector.report(diagnostic, context)
}
}

private fun reportSyntaxErrors(
file: PsiElement,
createAndReportSyntaxError: (PsiErrorElement, message: String) -> Unit,
): SyntaxErrorReport {
class ErrorReportingVisitor : AnalyzingUtils.PsiErrorElementVisitor() {
var hasErrors = false
var allErrorsAtEof = true

private fun PsiElement.isAtEof(): Boolean {
var element = this
Expand All @@ -242,8 +271,12 @@ class AnalyzerWithCompilerReport(

override fun visitErrorElement(element: PsiErrorElement) {
val description = element.errorDescription
reportDiagnostic(
element, SYNTAX_ERROR_FACTORY,
if (allErrorsAtEof && !element.isAtEof()) {
allErrorsAtEof = false
}
hasErrors = true
createAndReportSyntaxError(
element,
if (StringUtil.isEmpty(description)) "Syntax error" else description
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.KtSourceFile
import org.jetbrains.kotlin.cli.common.*
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.DefaultDiagnosticReporter
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
Expand All @@ -34,6 +35,7 @@ import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.config.messageCollector
import org.jetbrains.kotlin.config.moduleName
import org.jetbrains.kotlin.config.useLightTree
import org.jetbrains.kotlin.diagnostics.impl.BaseDiagnosticsCollector
import org.jetbrains.kotlin.fir.DependencyListForCliModule
import org.jetbrains.kotlin.fir.extensions.FirAnalysisHandlerExtension
import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar
Expand Down Expand Up @@ -70,7 +72,8 @@ object JvmFrontendPipelinePhase : PipelinePhase<ConfigurationPipelineArtifact, J
val (environment, sourcesProvider) = createEnvironmentAndSources(
configuration,
rootDisposable,
targetDescription
targetDescription,
diagnosticsCollector
) ?: return null

FirAnalysisHandlerExtension.analyze(environment.project, configuration)?.let {
Expand Down Expand Up @@ -193,7 +196,8 @@ object JvmFrontendPipelinePhase : PipelinePhase<ConfigurationPipelineArtifact, J
private fun createEnvironmentAndSources(
configuration: CompilerConfiguration,
rootDisposable: Disposable,
targetDescription: String
targetDescription: String,
diagnosticReporter: BaseDiagnosticsCollector
): EnvironmentAndSources? {
val messageCollector = configuration.messageCollector
return when (configuration.useLightTree) {
Expand All @@ -220,7 +224,7 @@ object JvmFrontendPipelinePhase : PipelinePhase<ConfigurationPipelineArtifact, J

val sources = {
val ktFiles = kotlinCoreEnvironment.getSourceFiles()
ktFiles.forEach { AnalyzerWithCompilerReport.reportSyntaxErrors(it, messageCollector) }
ktFiles.forEach { AnalyzerWithCompilerReport.reportSyntaxErrors(it, diagnosticReporter) }
groupKtFiles(ktFiles)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ package org.jetbrains.kotlin.fir.builder
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.util.PrivateForInline
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.DiagnosticList
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.PositioningStrategy

@Suppress("UNUSED_VARIABLE", "LocalVariableName", "ClassName", "unused")
@OptIn(PrivateForInline::class)
object SYNTAX_DIAGNOSTIC_LIST : DiagnosticList("FirSyntaxErrors") {
val Syntax by object : DiagnosticGroup("Syntax") {
val SYNTAX by error<PsiElement> {
val SYNTAX by error<PsiElement>(positioningStrategy = PositioningStrategy.SYNTAX_ERROR) {
parameter<String>("message")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ data class DiagnosticParameter(

enum class PositioningStrategy {
DEFAULT,
SYNTAX_ERROR,
VAL_OR_VAR_NODE,
SECONDARY_CONSTRUCTOR_DELEGATION_CALL,
DECLARATION_NAME,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import org.jetbrains.kotlin.diagnostics.rendering.RootDiagnosticRendererFactory
@Suppress("IncorrectFormatting")
object FirSyntaxErrors {
// Syntax
val SYNTAX: KtDiagnosticFactory1<String> = KtDiagnosticFactory1("SYNTAX", ERROR, SourceElementPositioningStrategies.DEFAULT, PsiElement::class)
val SYNTAX: KtDiagnosticFactory1<String> = KtDiagnosticFactory1("SYNTAX", ERROR, SourceElementPositioningStrategies.SYNTAX_ERROR, PsiElement::class)

init {
RootDiagnosticRendererFactory.registerFactory(FirSyntaxErrorsDefaultMessages)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,18 @@ object PositioningStrategies {
}
}

val SYNTAX_ERROR: PositioningStrategy<PsiElement> = object : PositioningStrategy<PsiElement>() {
override fun mark(element: PsiElement): List<TextRange> {
return DEFAULT.mark(element)
}

@DiagnosticLossRisk
override fun isValid(element: PsiElement): Boolean {
if (element is PsiErrorElement) return true
return DEFAULT.isValid(element)
}
}

@JvmField
val SUPERTYPES_LIST: PositioningStrategy<PsiElement> = object : PositioningStrategy<PsiElement>() {
override fun mark(element: PsiElement): List<TextRange> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ object SourceElementPositioningStrategies {
PositioningStrategies.DEFAULT
)

val SYNTAX_ERROR = SourceElementPositioningStrategy(
LightTreePositioningStrategies.DEFAULT,
PositioningStrategies.SYNTAX_ERROR
)

val VAL_OR_VAR_NODE = SourceElementPositioningStrategy(
LightTreePositioningStrategies.VAL_OR_VAR_NODE,
PositioningStrategies.VAL_OR_VAR_NODE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,37 @@ Buildfile: [TestData]/build.xml
build:
[javac] Compiling 2 source files to [Temp]
[javac] Compiling [[TestData]] => [[Temp]]
[javac] [TestData]/incorrectKotlinCode.kt:4:1: error: parameter name expected
[javac] [TestData]/incorrectKotlinCode.kt:3:1: error: function 'main' must have a body.
[javac] fun main(
[javac] ^^^^^^^^^
[javac] [TestData]/incorrectKotlinCode.kt:4:1: error: syntax error: Parameter name expected.
[javac]
[javac] ^
[javac] [TestData]/incorrectKotlinCode.kt:4:1: error: expecting comma or ')'
[javac] [TestData]/incorrectKotlinCode.kt:4:1: error: syntax error: Expecting comma or ')'.
[javac]
[javac] ^
[javac] [TestData]/incorrectKotlinCode.kt:4:1: error: expecting ')'
[javac] [TestData]/incorrectKotlinCode.kt:4:1: error: syntax error: Expecting ')'.
[javac]
[javac] ^
[javac] [TestData]/incorrectKotlinCode.kt:4:1: error: an explicit type is required on a value parameter.
[javac]
[javac] ^
[javac] [TestData]/incorrectKotlinCode.kt:3:1: error: function 'main' must have a body.
[javac] fun main(
[javac] ^^^^^^^^^
[kotlinc] Compiling [[TestData]] => [[Temp]]
[kotlinc] [TestData]/incorrectKotlinCode.kt:4:1: error: parameter name expected
[kotlinc] [TestData]/incorrectKotlinCode.kt:3:1: error: function 'main' must have a body.
[kotlinc] fun main(
[kotlinc] ^^^^^^^^^
[kotlinc] [TestData]/incorrectKotlinCode.kt:4:1: error: syntax error: Parameter name expected.
[kotlinc]
[kotlinc] ^
[kotlinc] [TestData]/incorrectKotlinCode.kt:4:1: error: expecting comma or ')'
[kotlinc] [TestData]/incorrectKotlinCode.kt:4:1: error: syntax error: Expecting comma or ')'.
[kotlinc]
[kotlinc] ^
[kotlinc] [TestData]/incorrectKotlinCode.kt:4:1: error: expecting ')'
[kotlinc] [TestData]/incorrectKotlinCode.kt:4:1: error: syntax error: Expecting ')'.
[kotlinc]
[kotlinc] ^
[kotlinc] [TestData]/incorrectKotlinCode.kt:4:1: error: an explicit type is required on a value parameter.
[kotlinc]
[kotlinc] ^
[kotlinc] [TestData]/incorrectKotlinCode.kt:3:1: error: function 'main' must have a body.
[kotlinc] fun main(
[kotlinc] ^^^^^^^^^

BUILD SUCCESSFUL
Total time: [time]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Buildfile: [TestData]/build.xml

build:
[kotlinc] Compiling [[TestData]] => [[Temp]]
[kotlinc] [TestData]/incorrectKotlinCode.kt:1:1: error: expecting a top level declaration
[kotlinc] [TestData]/incorrectKotlinCode.kt:1:1: error: syntax error: Expecting a top level declaration.
[kotlinc] xxxx
[kotlinc] ^^^^

Expand Down

0 comments on commit 2b0897c

Please sign in to comment.